[
  {
    "path": ".config/dotnet-tools.json",
    "content": "{\n  \"version\": 1,\n  \"isRoot\": true,\n  \"tools\": {\n    \"husky\": {\n      \"version\": \"0.6.4\",\n      \"commands\": [\n        \"husky\"\n      ]\n    }\n  }\n}"
  },
  {
    "path": ".copilot-instructions.md",
    "content": "# WeihanLi.Common - Copilot Instructions\n\n## Project Overview\n\nWeihanLi.Common is a comprehensive .NET utility library providing common helpers, extensions, and utilities for .NET applications. The library includes dependency injection, AOP (Aspect-Oriented Programming), event handling, logging, data access extensions, TOTP implementation, template engine, and much more.\n\n## Project Structure\n\n```\n├── src/                           # Main source code\n│   ├── WeihanLi.Common/           # Core library\n│   ├── WeihanLi.Common.Logging.Serilog/  # Serilog integration\n│   └── WeihanLi.Extensions.Hosting/       # Hosting extensions\n├── test/                          # Unit tests\n│   └── WeihanLi.Common.Test/      # Test projects using xUnit\n├── samples/                       # Sample applications\n│   ├── AspNetCoreSample/          # ASP.NET Core examples\n│   └── DotNetCoreSample/          # Console app examples\n├── perf/                          # Performance benchmarks\n├── docs/                          # Documentation using DocFX\n├── build/                         # Build scripts and tools\n└── .github/                       # CI/CD workflows\n```\n\n## Key Components\n\n### Core Features\n- **Dependency Injection**: Custom DI container similar to Microsoft's framework\n- **Fluent Aspects (AOP)**: Dynamic proxy-based AOP framework\n- **Event System**: EventBus, EventQueue, and EventStore implementations\n- **Logging Framework**: Integration with Serilog and Microsoft logging\n- **Data Extensions**: Dapper-like ADO.NET extensions for database operations\n- **TOTP Implementation**: Time-based One-Time Password algorithm\n- **Template Engine**: Custom template processing engine\n- **HTTP Utilities**: HTTP client extensions and utilities\n- **Guard Utilities**: Parameter validation helpers\n- **Compression**: Data compression utilities\n- **Extensions**: Extensive extension methods for common types\n\n### Namespace Organization\n- `WeihanLi.Common` - Core utilities and Guard classes\n- `WeihanLi.Common.Aspect` - AOP framework components\n- `WeihanLi.Common.Data` - Database and data access utilities\n- `WeihanLi.Common.DependencyInjection` - DI container implementation\n- `WeihanLi.Common.Event` - Event handling system\n- `WeihanLi.Common.Extensions` - Extension methods for various types\n- `WeihanLi.Common.Helpers` - Utility helper classes\n- `WeihanLi.Common.Http` - HTTP-related utilities\n- `WeihanLi.Common.Logging` - Logging abstractions and implementations\n- `WeihanLi.Common.Otp` - TOTP and OTP implementations\n- `WeihanLi.Common.Services` - Common service implementations\n- `WeihanLi.Common.Template` - Template engine components\n\n## Code Style and Conventions\n\n### General Guidelines\n- **Target Frameworks**: netstandard2.0, net8.0, net9.0, net10.0\n- **Language Features**: C# with nullable reference types enabled, implicit usings\n- **License**: Apache License 2.0\n- **Naming**: PascalCase for public members, camelCase for private fields, following the editorconfig\n- **Null Safety**: Extensive use of nullable annotations and Guard utilities\n\n### Common Patterns\n\n#### Guard Usage\nAlways validate parameters using the Guard class:\n```csharp\npublic static string Process(string input)\n{\n    Guard.NotNullOrEmpty(input);\n    // implementation\n}\n```\n\n#### Extension Method Pattern\nExtension methods should be in dedicated files with descriptive names:\n```csharp\nnamespace WeihanLi.Extensions;\n\npublic static class StringExtension\n{\n    public static bool IsNullOrEmpty(this string? str) => string.IsNullOrEmpty(str);\n}\n```\n\n#### Configuration Pattern\nUse options pattern for configuration:\n```csharp\npublic sealed class ServiceOptions\n{\n    public string ConnectionString { get; set; } = string.Empty;\n    public int Timeout { get; set; } = 30;\n}\n```\n\n#### Fluent API Design\nMany components use fluent interfaces:\n```csharp\nFluentAspects.Configure(options =>\n    options.InterceptAll()\n           .With<LoggingInterceptor>()\n);\n```\n\n## Testing Guidelines\n\n### Test Structure\n- Use xUnit as the testing framework\n- Test files should be named `{ComponentName}Test.cs`\n- Tests should be in the `WeihanLi.Common.Test` namespace\n- Use descriptive test method names that explain the scenario\n\n### Test Patterns\n```csharp\n[Fact]\npublic void MethodName_Scenario_ExpectedResult()\n{\n    // Arrange\n    var input = \"test\";\n    \n    // Act\n    var result = SystemUnderTest.Process(input);\n    \n    // Assert\n    Assert.NotNull(result);\n}\n\n[Theory]\n[InlineData(\"input1\", \"expected1\")]\n[InlineData(\"input2\", \"expected2\")]\npublic void MethodName_MultipleInputs_ReturnsExpected(string input, string expected)\n{\n    var result = SystemUnderTest.Process(input);\n    Assert.Equal(expected, result);\n}\n```\n\n## Build and Development\n\n### Build System\n- Uses .NET 10 SDK\n- Custom build scripts in `build/` directory using dotnet-execute\n- Multi-targeting for compatibility across .NET versions\n- Automated CI/CD with GitHub Actions and Azure DevOps\n\n### .NET SDK Setup\nThe project requires .NET 10 SDK (with rollForward enabled to support newer versions). For development setup:\n\n1. **Install .NET SDK**: Download and install .NET 10 SDK or later from [dotnet.microsoft.com](https://dotnet.microsoft.com/download)\n2. **Multiple SDK Versions**: The GitHub Actions workflow (`.github/workflows/default.yml`) shows the supported SDK versions:\n   ```yaml\n   dotnet-version: |\n     8.0.x\n     9.0.x\n     10.0.x\n   ```\n3. **Verify Installation**: Run `dotnet --info` to confirm the SDK is properly installed\n4. **SDK Configuration**: The `global.json` file specifies the minimum SDK version with `rollForward: \"latestMajor\"` enabled\n\n### Development Commands\n```bash\n# Build the solution\ndotnet build\n\n# Run tests\ndotnet test\n\n# Run custom build script\ndotnet build.cs\n\n# Format code\ndotnet format\n```\n\n### Code Generation\nSome files are generated using T4 templates (.tt files):\n- Database extension methods\n- Service container registration methods\n\n## Common Tasks\n\n### Adding New Extensions\n1. Create extension class in appropriate `Extensions/` subdirectory\n2. Use proper namespace (typically `WeihanLi.Extensions`)\n3. Add comprehensive XML documentation\n4. Write corresponding unit tests\n5. Follow existing patterns for parameter validation\n\n### Adding New Services\n1. Define interface in `Abstractions/` if needed\n2. Implement service in `Services/` directory\n3. Add configuration options class if configurable\n4. Register with DI container if applicable\n5. Add integration tests\n\n### Working with AOP\nThe Fluent Aspects framework allows method interception:\n```csharp\n// Configure interceptors\nFluentAspects.Configure(options =>\n{\n    options.InterceptMethod<IService>(s => s.Process(Argument.Any<string>()))\n           .With<ValidationInterceptor>();\n});\n\n// Create proxy\nvar service = FluentAspects.AspectOptions.ProxyFactory\n    .CreateProxy<IService>(new ServiceImplementation());\n```\n\n### Database Operations\nUse the data extensions for database operations:\n```csharp\n// Query data\nvar users = connection.Select<User>(\"SELECT * FROM Users WHERE Age > @age\", new { age = 18 });\n\n// Repository pattern\nvar repository = new Repository<User>(() => connectionFactory.GetConnection());\nvar user = repository.Fetch(u => u.Id == userId);\n```\n\n## Dependencies and Compatibility\n\n### Key Dependencies\n- Microsoft.Extensions.Configuration\n- Microsoft.Extensions.Logging\n- Newtonsoft.Json\n- System.ComponentModel.Annotations (for .NET Standard 2.0)\n\n### Compatibility Notes\n- Supports .NET Standard 2.0 for broad compatibility\n- Modern .NET versions (8.0+) for latest features\n- Conditional compilation for framework-specific optimizations\n- AOT compatibility for .NET 8.0+\n\n## Documentation\n\n- XML documentation is required for all public APIs\n- Use DocFX for generating documentation website\n- Examples should be provided in the `samples/` directory\n- README files should be updated when adding major features\n\n## Performance Considerations\n\n- Use `Span<T>` and `Memory<T>` where appropriate for modern .NET versions\n- Avoid allocations in hot paths\n- Use object pooling for frequently allocated objects\n- Benchmark performance-critical code in `perf/` directory\n\n## Security Guidelines\n\n- Input validation using Guard utilities\n- Proper disposal of resources\n- Secure random number generation for cryptographic operations\n- Avoid hardcoded secrets or credentials\n\n## Contributing Guidelines\n\nWhen contributing to this repository:\n1. Follow existing code style and patterns\n2. Add comprehensive tests for new functionality\n3. Update documentation and examples\n4. Ensure compatibility across target frameworks\n5. Use conventional commit messages\n6. Consider performance implications\n7. Validate with existing build and test processes"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n    \"name\": \"CodeSpace\",\n    \"image\": \"mcr.microsoft.com/devcontainers/base:debian\",\n    \"postCreateCommand\": \"bash -i ${containerWorkspaceFolder}/.devcontainer/scripts/post-creation.sh\",\n    // features list: https://containers.dev/features\n    \"features\": {\n        \"ghcr.io/devcontainers/features/git:1\": {},\n        \"ghcr.io/devcontainers/features/docker-in-docker:2\": {},\n        \"ghcr.io/devcontainers/features/dotnet:2\": {\n          \"version\": \"latest\",\n          \"additionalVersions\": \"lts\"\n        }\n    },\n    \"customizations\": {\n      \"vscode\": {\n        \"extensions\": [\n          \"ms-dotnettools.csdevkit\",\n          \"EditorConfig.EditorConfig\",\n          \"DavidAnson.vscode-markdownlint\"\n        ]\n      }\n    }\n}"
  },
  {
    "path": ".devcontainer/scripts/post-creation.sh",
    "content": "dotnet tool install -g dotnet-execute --prereleaase\ndotnet tool install -g dotnet-httpie\n\ndotnet restore\n"
  },
  {
    "path": ".editorconfig",
    "content": "# EditorConfig is awesome:http://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Don't use tabs for indentation.\n[*]\nindent_style = space\n# (Please don't specify an indent_size here; that has too many unintended consequences.)\n\n# Code files\n[*.{cs,csx,vb,vbx}]\nindent_size = 4\ninsert_final_newline = true\ncharset = utf-8-bom\n\n# Xml project files\n[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]\nindent_size = 2\n\n# Xml config files\n[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]\nindent_size = 2\n\n# JSON files\n[*.json]\nindent_size = 2\n\n# Dotnet code style settings:\n[*.{cs,vb}]\n# File header\nfile_header_template = Copyright (c) Weihan Li. All rights reserved.\\nLicensed under the Apache license.\n\n# Sort using and Import directives with System.* appearing first\ndotnet_sort_system_directives_first = false\n# Avoid \"this.\" and \"Me.\" if not necessary\ndotnet_style_qualification_for_field = false:suggestion\ndotnet_style_qualification_for_property = false:suggestion\ndotnet_style_qualification_for_method = false:suggestion\ndotnet_style_qualification_for_event = false:suggestion\n\n# Use language keywords instead of framework type names for type references\ndotnet_style_predefined_type_for_locals_parameters_members = true:suggestion\ndotnet_style_predefined_type_for_member_access = true:suggestion\n\n# Suggest more modern language features when available\ndotnet_style_object_initializer = true:suggestion\ndotnet_style_collection_initializer = true:suggestion\ndotnet_style_coalesce_expression = true:suggestion\ndotnet_style_null_propagation = true:suggestion\ndotnet_style_explicit_tuple_names = true:suggestion\n\n# CSharp code style settings:\n[*.cs]\n# namespace style\ncsharp_style_namespace_declarations=file_scoped:warning\n\n# Prefer \"var\" everywhere\ncsharp_style_var_for_built_in_types = true:suggestion\ncsharp_style_var_when_type_is_apparent = true:suggestion\ncsharp_style_var_elsewhere = true:suggestion\n\n# Prefer method-like constructs to have a block body\ncsharp_style_expression_bodied_methods = false:none\ncsharp_style_expression_bodied_constructors = false:none\ncsharp_style_expression_bodied_operators = false:none\n\n# Prefer property-like constructs to have an expression-body\ncsharp_style_expression_bodied_properties = true:none\ncsharp_style_expression_bodied_indexers = true:none\ncsharp_style_expression_bodied_accessors = true:none\n\n# Suggest more modern language features when available\ncsharp_style_pattern_matching_over_is_with_cast_check = true:suggestion\ncsharp_style_pattern_matching_over_as_with_null_check = true:suggestion\ncsharp_style_inlined_variable_declaration = true:suggestion\ncsharp_style_throw_expression = true:suggestion\ncsharp_style_conditional_delegate_call = true:suggestion\n\n# Newline settings\ncsharp_new_line_before_open_brace = all\ncsharp_new_line_before_else = true\ncsharp_new_line_before_catch = true\ncsharp_new_line_before_finally = true\ncsharp_new_line_before_members_in_object_initializers = true\ncsharp_new_line_before_members_in_anonymous_types = true\n\n# Shell scripts\n[*.sh]\nend_of_line=lf\n[*.{cmd, bat}]\nend_of_line=crlf\n\n# https://github.com/dotnet/runtime/blob/main/eng/CodeAnalysis.src.globalconfig\n# AD0001: Analyzer threw an exception\ndotnet_diagnostic.AD0001.severity = suggestion\n\n# BCL0001: Ensure minimum API surface is respected\ndotnet_diagnostic.BCL0001.severity = warning\n\n# BCL0010: AppContext default value expected to be true\ndotnet_diagnostic.BCL0010.severity = warning\n\n# BCL0011: AppContext default value defined in if statement with incorrect pattern\ndotnet_diagnostic.BCL0011.severity = warning\n\n# BCL0012: AppContext default value defined in if statement at root of switch case\ndotnet_diagnostic.BCL0012.severity = warning\n\n# BCL0015: Invalid P/Invoke call\ndotnet_diagnostic.BCL0015.severity = none\n\n# BCL0020: Invalid SR.Format call\ndotnet_diagnostic.BCL0020.severity = warning\n\n# CA1000: Do not declare static members on generic types\ndotnet_diagnostic.CA1000.severity = none\n\n# CA1001: Types that own disposable fields should be disposable\ndotnet_diagnostic.CA1001.severity = none\n\n# CA1002: Do not expose generic lists\ndotnet_diagnostic.CA1002.severity = none\n\n# CA1003: Use generic event handler instances\ndotnet_diagnostic.CA1003.severity = none\n\n# CA1005: Avoid excessive parameters on generic types\ndotnet_diagnostic.CA1005.severity = none\n\n# CA1008: Enums should have zero value\ndotnet_diagnostic.CA1008.severity = none\n\n# CA1010: Generic interface should also be implemented\ndotnet_diagnostic.CA1010.severity = none\n\n# CA1012: Abstract types should not have public constructors\ndotnet_diagnostic.CA1012.severity = none\n\n# CA1014: Mark assemblies with CLSCompliant\ndotnet_diagnostic.CA1014.severity = none\n\n# CA1016: Mark assemblies with assembly version\ndotnet_diagnostic.CA1016.severity = none\n\n# CA1017: Mark assemblies with ComVisible\ndotnet_diagnostic.CA1017.severity = none\n\n# CA1018: Mark attributes with AttributeUsageAttribute\ndotnet_diagnostic.CA1018.severity = warning\n\n# CA1019: Define accessors for attribute arguments\ndotnet_diagnostic.CA1019.severity = none\n\n# CA1021: Avoid out parameters\ndotnet_diagnostic.CA1021.severity = none\n\n# CA1024: Use properties where appropriate\ndotnet_diagnostic.CA1024.severity = none\n\n# CA1027: Mark enums with FlagsAttribute\ndotnet_diagnostic.CA1027.severity = none\n\n# CA1028: Enum Storage should be Int32\ndotnet_diagnostic.CA1028.severity = none\n\n# CA1030: Use events where appropriate\ndotnet_diagnostic.CA1030.severity = none\n\n# CA1031: Do not catch general exception types\ndotnet_diagnostic.CA1031.severity = none\n\n# CA1032: Implement standard exception constructors\ndotnet_diagnostic.CA1032.severity = none\n\n# CA1033: Interface methods should be callable by child types\ndotnet_diagnostic.CA1033.severity = none\n\n# CA1034: Nested types should not be visible\ndotnet_diagnostic.CA1034.severity = none\n\n# CA1036: Override methods on comparable types\ndotnet_diagnostic.CA1036.severity = none\n\n# CA1040: Avoid empty interfaces\ndotnet_diagnostic.CA1040.severity = none\n\n# CA1041: Provide ObsoleteAttribute message\ndotnet_diagnostic.CA1041.severity = none\n\n# CA1043: Use Integral Or String Argument For Indexers\ndotnet_diagnostic.CA1043.severity = none\n\n# CA1044: Properties should not be write only\ndotnet_diagnostic.CA1044.severity = none\n\n# CA1045: Do not pass types by reference\ndotnet_diagnostic.CA1045.severity = none\n\n# CA1046: Do not overload equality operator on reference types\ndotnet_diagnostic.CA1046.severity = none\n\n# CA1047: Do not declare protected member in sealed type\ndotnet_diagnostic.CA1047.severity = warning\n\n# CA1050: Declare types in namespaces\ndotnet_diagnostic.CA1050.severity = warning\n\n# CA1051: Do not declare visible instance fields\ndotnet_diagnostic.CA1051.severity = none\n\n# CA1052: Static holder types should be Static or NotInheritable\ndotnet_diagnostic.CA1052.severity = warning\ndotnet_code_quality.CA1052.api_surface = private, internal\n\n# CA1054: URI-like parameters should not be strings\ndotnet_diagnostic.CA1054.severity = none\n\n# CA1055: URI-like return values should not be strings\ndotnet_diagnostic.CA1055.severity = none\n\n# CA1056: URI-like properties should not be strings\ndotnet_diagnostic.CA1056.severity = none\n\n# CA1058: Types should not extend certain base types\ndotnet_diagnostic.CA1058.severity = none\n\n# CA1060: Move pinvokes to native methods class\ndotnet_diagnostic.CA1060.severity = none\n\n# CA1061: Do not hide base class methods\ndotnet_diagnostic.CA1061.severity = none\n\n# CA1062: Validate arguments of public methods\ndotnet_diagnostic.CA1062.severity = none\n\n# CA1063: Implement IDisposable Correctly\ndotnet_diagnostic.CA1063.severity = none\n\n# CA1064: Exceptions should be public\ndotnet_diagnostic.CA1064.severity = none\n\n# CA1065: Do not raise exceptions in unexpected locations\ndotnet_diagnostic.CA1065.severity = none\n\n# CA1066: Implement IEquatable when overriding Object.Equals\ndotnet_diagnostic.CA1066.severity = warning\n\n# CA1067: Override Object.Equals(object) when implementing IEquatable<T>\ndotnet_diagnostic.CA1067.severity = warning\n\n# CA1068: CancellationToken parameters must come last\ndotnet_diagnostic.CA1068.severity = none\n\n# CA1069: Enums values should not be duplicated\ndotnet_diagnostic.CA1069.severity = none\n\n# CA1070: Do not declare event fields as virtual\ndotnet_diagnostic.CA1070.severity = suggestion\n\n# CA1200: Avoid using cref tags with a prefix\ndotnet_diagnostic.CA1200.severity = suggestion\n\n# CA1303: Do not pass literals as localized parameters\ndotnet_diagnostic.CA1303.severity = none\n\n# CA1304: Specify CultureInfo\ndotnet_diagnostic.CA1304.severity = none\n\n# CA1305: Specify IFormatProvider\ndotnet_diagnostic.CA1305.severity = none\n\n# CA1307: Specify StringComparison for clarity\ndotnet_diagnostic.CA1307.severity = none\n\n# CA1308: Normalize strings to uppercase\ndotnet_diagnostic.CA1308.severity = none\n\n# CA1309: Use ordinal string comparison\ndotnet_diagnostic.CA1309.severity = none\n\n# CA1310: Specify StringComparison for correctness\ndotnet_diagnostic.CA1310.severity = suggestion\n\n# CA1311: Specify a culture or use an invariant version\ndotnet_diagnostic.CA1311.severity = warning\n\n# CA1401: P/Invokes should not be visible\ndotnet_diagnostic.CA1401.severity = warning\n\n# CA1416: Validate platform compatibility\ndotnet_diagnostic.CA1416.severity = warning\n\n# CA1417: Do not use 'OutAttribute' on string parameters for P/Invokes\ndotnet_diagnostic.CA1417.severity = warning\n\n# CA1418: Use valid platform string\ndotnet_diagnostic.CA1418.severity = warning\n\n# CA1419: Provide a parameterless constructor that is as visible as the containing type for concrete types derived from 'System.Runtime.InteropServices.SafeHandle'\ndotnet_diagnostic.CA1419.severity = warning\n\n# CA1420: Property, type, or attribute requires runtime marshalling\ndotnet_diagnostic.CA1420.severity = warning\n\n# CA1421: This method uses runtime marshalling even when the 'DisableRuntimeMarshallingAttribute' is applied\ndotnet_diagnostic.CA1421.severity = suggestion\n\n# CA1501: Avoid excessive inheritance\ndotnet_diagnostic.CA1501.severity = none\n\n# CA1502: Avoid excessive complexity\ndotnet_diagnostic.CA1502.severity = none\n\n# CA1505: Avoid unmaintainable code\ndotnet_diagnostic.CA1505.severity = none\n\n# CA1506: Avoid excessive class coupling\ndotnet_diagnostic.CA1506.severity = none\n\n# CA1507: Use nameof to express symbol names\ndotnet_diagnostic.CA1507.severity = warning\n\n# CA1508: Avoid dead conditional code\ndotnet_diagnostic.CA1508.severity = none\n\n# CA1509: Invalid entry in code metrics rule specification file\ndotnet_diagnostic.CA1509.severity = none\n\n# CA1700: Do not name enum values 'Reserved'\ndotnet_diagnostic.CA1700.severity = none\n\n# CA1707: Identifiers should not contain underscores\ndotnet_diagnostic.CA1707.severity = none\n\n# CA1708: Identifiers should differ by more than case\ndotnet_diagnostic.CA1708.severity = none\n\n# CA1710: Identifiers should have correct suffix\ndotnet_diagnostic.CA1710.severity = none\n\n# CA1711: Identifiers should not have incorrect suffix\ndotnet_diagnostic.CA1711.severity = none\n\n# CA1712: Do not prefix enum values with type name\ndotnet_diagnostic.CA1712.severity = none\n\n# CA1713: Events should not have 'Before' or 'After' prefix\ndotnet_diagnostic.CA1713.severity = none\n\n# CA1715: Identifiers should have correct prefix\ndotnet_diagnostic.CA1715.severity = none\n\n# CA1716: Identifiers should not match keywords\ndotnet_diagnostic.CA1716.severity = none\n\n# CA1720: Identifier contains type name\ndotnet_diagnostic.CA1720.severity = none\n\n# CA1721: Property names should not match get methods\ndotnet_diagnostic.CA1721.severity = none\n\n# CA1724: Type names should not match namespaces\ndotnet_diagnostic.CA1724.severity = none\n\n# CA1725: Parameter names should match base declaration\ndotnet_diagnostic.CA1725.severity = suggestion\n\n# CA1727: Use PascalCase for named placeholders\ndotnet_diagnostic.CA1727.severity = suggestion\n\n# CA1802: Use literals where appropriate\ndotnet_diagnostic.CA1802.severity = warning\ndotnet_code_quality.CA1802.api_surface = private, internal\n\n# CA1805: Do not initialize unnecessarily\ndotnet_diagnostic.CA1805.severity = warning\n\n# CA1806: Do not ignore method results\ndotnet_diagnostic.CA1806.severity = none\n\n# CA1810: Initialize reference type static fields inline\ndotnet_diagnostic.CA1810.severity = warning\n\n# CA1812: Avoid uninstantiated internal classes\ndotnet_diagnostic.CA1812.severity = none\n\n# CA1813: Avoid unsealed attributes\ndotnet_diagnostic.CA1813.severity = none\n\n# CA1814: Prefer jagged arrays over multidimensional\ndotnet_diagnostic.CA1814.severity = none\n\n# CA1815: Override equals and operator equals on value types\ndotnet_diagnostic.CA1815.severity = none\n\n# CA1816: Dispose methods should call SuppressFinalize\ndotnet_diagnostic.CA1816.severity = none\n\n# CA1819: Properties should not return arrays\ndotnet_diagnostic.CA1819.severity = none\n\n# CA1820: Test for empty strings using string length\ndotnet_diagnostic.CA1820.severity = none\n\n# CA1821: Remove empty Finalizers\ndotnet_diagnostic.CA1821.severity = warning\n\n# CA1822: Mark members as static\ndotnet_diagnostic.CA1822.severity = warning\ndotnet_code_quality.CA1822.api_surface = private, internal\n\n# CA1823: Avoid unused private fields\ndotnet_diagnostic.CA1823.severity = warning\n\n# CA1824: Mark assemblies with NeutralResourcesLanguageAttribute\ndotnet_diagnostic.CA1824.severity = warning\n\n# CA1825: Avoid zero-length array allocations\ndotnet_diagnostic.CA1825.severity = warning\n\n# CA1826: Do not use Enumerable methods on indexable collections\ndotnet_diagnostic.CA1826.severity = warning\n\n# CA1827: Do not use Count() or LongCount() when Any() can be used\ndotnet_diagnostic.CA1827.severity = warning\n\n# CA1828: Do not use CountAsync() or LongCountAsync() when AnyAsync() can be used\ndotnet_diagnostic.CA1828.severity = warning\n\n# CA1829: Use Length/Count property instead of Count() when available\ndotnet_diagnostic.CA1829.severity = warning\n\n# CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder\ndotnet_diagnostic.CA1830.severity = warning\n\n# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate\ndotnet_diagnostic.CA1831.severity = warning\n\n# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate\ndotnet_diagnostic.CA1832.severity = warning\n\n# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate\ndotnet_diagnostic.CA1833.severity = warning\n\n# CA1834: Consider using 'StringBuilder.Append(char)' when applicable\ndotnet_diagnostic.CA1834.severity = warning\n\n# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync'\ndotnet_diagnostic.CA1835.severity = warning\n\n# CA1836: Prefer IsEmpty over Count\ndotnet_diagnostic.CA1836.severity = warning\n\n# CA1837: Use 'Environment.ProcessId'\ndotnet_diagnostic.CA1837.severity = warning\n\n# CA1838: Avoid 'StringBuilder' parameters for P/Invokes\ndotnet_diagnostic.CA1838.severity = warning\n\n# CA1839: Use 'Environment.ProcessPath'\ndotnet_diagnostic.CA1839.severity = warning\n\n# CA1840: Use 'Environment.CurrentManagedThreadId'\ndotnet_diagnostic.CA1840.severity = warning\n\n# CA1841: Prefer Dictionary.Contains methods\ndotnet_diagnostic.CA1841.severity = warning\n\n# CA1842: Do not use 'WhenAll' with a single task\ndotnet_diagnostic.CA1842.severity = warning\n\n# CA1843: Do not use 'WaitAll' with a single task\ndotnet_diagnostic.CA1843.severity = warning\n\n# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream'\ndotnet_diagnostic.CA1844.severity = warning\n\n# CA1845: Use span-based 'string.Concat'\ndotnet_diagnostic.CA1845.severity = warning\n\n# CA1846: Prefer 'AsSpan' over 'Substring'\ndotnet_diagnostic.CA1846.severity = warning\n\n# CA1847: Use char literal for a single character lookup\ndotnet_diagnostic.CA1847.severity = warning\n\n# CA1848: Use the LoggerMessage delegates\ndotnet_diagnostic.CA1848.severity = none\n\n# CA1849: Call async methods when in an async method\ndotnet_diagnostic.CA1849.severity = suggestion\n\n# CA1850: Prefer static 'HashData' method over 'ComputeHash'\ndotnet_diagnostic.CA1850.severity = warning\n\n# CA1851: Possible multiple enumerations of 'IEnumerable' collection\ndotnet_diagnostic.CA1851.severity = suggestion\n\n# CA1852: Seal internal types\ndotnet_diagnostic.CA1852.severity = warning\n\n# CA1853: Unnecessary call to 'Dictionary.ContainsKey(key)'\ndotnet_diagnostic.CA1853.severity = warning\n\n# CA1854: Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method\ndotnet_diagnostic.CA1854.severity = warning\n\n# CA2000: Dispose objects before losing scope\ndotnet_diagnostic.CA2000.severity = none\n\n# CA2002: Do not lock on objects with weak identity\ndotnet_diagnostic.CA2002.severity = none\n\n# CA2007: Consider calling ConfigureAwait on the awaited task\ndotnet_diagnostic.CA2007.severity = warning\n\n# CA2008: Do not create tasks without passing a TaskScheduler\ndotnet_diagnostic.CA2008.severity = warning\n\n# CA2009: Do not call ToImmutableCollection on an ImmutableCollection value\ndotnet_diagnostic.CA2009.severity = warning\n\n# CA2011: Avoid infinite recursion\ndotnet_diagnostic.CA2011.severity = warning\n\n# CA2012: Use ValueTasks correctly\ndotnet_diagnostic.CA2012.severity = warning\n\n# CA2013: Do not use ReferenceEquals with value types\ndotnet_diagnostic.CA2013.severity = warning\n\n# CA2014: Do not use stackalloc in loops\ndotnet_diagnostic.CA2014.severity = warning\n\n# CA2015: Do not define finalizers for types derived from MemoryManager<T>\ndotnet_diagnostic.CA2015.severity = warning\n\n# CA2016: Forward the 'CancellationToken' parameter to methods\ndotnet_diagnostic.CA2016.severity = warning\n\n# CA2017: Parameter count mismatch\ndotnet_diagnostic.CA2017.severity = warning\n\n# CA2018: 'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument\ndotnet_diagnostic.CA2018.severity = warning\n\n# CA2019: Improper 'ThreadStatic' field initialization\ndotnet_diagnostic.CA2019.severity = warning\n\n# CA2100: Review SQL queries for security vulnerabilities\ndotnet_diagnostic.CA2100.severity = none\n\n# CA2101: Specify marshaling for P/Invoke string arguments\ndotnet_diagnostic.CA2101.severity = none\n\n# CA2109: Review visible event handlers\ndotnet_diagnostic.CA2109.severity = none\n\n# CA2119: Seal methods that satisfy private interfaces\ndotnet_diagnostic.CA2119.severity = none\n\n# CA2153: Do Not Catch Corrupted State Exceptions\ndotnet_diagnostic.CA2153.severity = none\n\n# CA2200: Rethrow to preserve stack details\ndotnet_diagnostic.CA2200.severity = warning\n\n# CA2201: Do not raise reserved exception types\ndotnet_diagnostic.CA2201.severity = none\n\n# CA2207: Initialize value type static fields inline\ndotnet_diagnostic.CA2207.severity = warning\n\n# CA2208: Instantiate argument exceptions correctly\ndotnet_diagnostic.CA2208.severity = warning\ndotnet_code_quality.CA2208.api_surface = public\n\n# CA2211: Non-constant fields should not be visible\ndotnet_diagnostic.CA2211.severity = none\n\n# CA2213: Disposable fields should be disposed\ndotnet_diagnostic.CA2213.severity = none\n\n# CA2214: Do not call overridable methods in constructors\ndotnet_diagnostic.CA2214.severity = none\n\n# CA2215: Dispose methods should call base class dispose\ndotnet_diagnostic.CA2215.severity = none\n\n# CA2216: Disposable types should declare finalizer\ndotnet_diagnostic.CA2216.severity = none\n\n# CA2217: Do not mark enums with FlagsAttribute\ndotnet_diagnostic.CA2217.severity = none\n\n# CA2218: Override GetHashCode on overriding Equals\ndotnet_diagnostic.CA2218.severity = none\n\n# CA2219: Do not raise exceptions in finally clauses\ndotnet_diagnostic.CA2219.severity = none\n\n# CA2224: Override Equals on overloading operator equals\ndotnet_diagnostic.CA2224.severity = none\n\n# CA2225: Operator overloads have named alternates\ndotnet_diagnostic.CA2225.severity = none\n\n# CA2226: Operators should have symmetrical overloads\ndotnet_diagnostic.CA2226.severity = none\n\n# CA2227: Collection properties should be read only\ndotnet_diagnostic.CA2227.severity = none\n\n# CA2229: Implement serialization constructors\ndotnet_diagnostic.CA2229.severity = warning\n\n# CA2231: Overload operator equals on overriding value type Equals\ndotnet_diagnostic.CA2231.severity = none\n\n# CA2234: Pass system uri objects instead of strings\ndotnet_diagnostic.CA2234.severity = none\n\n# CA2235: Mark all non-serializable fields\ndotnet_diagnostic.CA2235.severity = none\n\n# CA2237: Mark ISerializable types with serializable\ndotnet_diagnostic.CA2237.severity = none\n\n# CA2241: Provide correct arguments to formatting methods\ndotnet_diagnostic.CA2241.severity = warning\n\n# CA2242: Test for NaN correctly\ndotnet_diagnostic.CA2242.severity = warning\n\n# CA2243: Attribute string literals should parse correctly\ndotnet_diagnostic.CA2243.severity = warning\n\n# CA2244: Do not duplicate indexed element initializations\ndotnet_diagnostic.CA2244.severity = warning\n\n# CA2245: Do not assign a property to itself\ndotnet_diagnostic.CA2245.severity = warning\n\n# CA2246: Assigning symbol and its member in the same statement\ndotnet_diagnostic.CA2246.severity = warning\n\n# CA2247: Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum\ndotnet_diagnostic.CA2247.severity = warning\n\n# CA2248: Provide correct 'enum' argument to 'Enum.HasFlag'\ndotnet_diagnostic.CA2248.severity = warning\n\n# CA2249: Consider using 'string.Contains' instead of 'string.IndexOf'\ndotnet_diagnostic.CA2249.severity = warning\n\n# CA2250: Use 'ThrowIfCancellationRequested'\ndotnet_diagnostic.CA2250.severity = warning\n\n# CA2251: Use 'string.Equals'\ndotnet_diagnostic.CA2251.severity = warning\n\n# CA2252: This API requires opting into preview features\ndotnet_diagnostic.CA2252.severity = error\n\n# CA2253: Named placeholders should not be numeric values\ndotnet_diagnostic.CA2253.severity = none\n\n# CA2254: Template should be a static expression\ndotnet_diagnostic.CA2254.severity = none\n\n# CA2255: The 'ModuleInitializer' attribute should not be used in libraries\ndotnet_diagnostic.CA2255.severity = warning\n\n# CA2256: All members declared in parent interfaces must have an implementation in a DynamicInterfaceCastableImplementation-attributed interface\ndotnet_diagnostic.CA2256.severity = warning\n\n# CA2257: Members defined on an interface with the 'DynamicInterfaceCastableImplementationAttribute' should be 'static'\ndotnet_diagnostic.CA2257.severity = warning\n\n# CA2258: Providing a 'DynamicInterfaceCastableImplementation' interface in Visual Basic is unsupported\ndotnet_diagnostic.CA2258.severity = warning\n\n# CA2259: 'ThreadStatic' only affects static fields\ndotnet_diagnostic.CA2259.severity = warning\n\n# CA2300: Do not use insecure deserializer BinaryFormatter\ndotnet_diagnostic.CA2300.severity = none\n\n# CA2301: Do not call BinaryFormatter.Deserialize without first setting BinaryFormatter.Binder\ndotnet_diagnostic.CA2301.severity = none\n\n# CA2302: Ensure BinaryFormatter.Binder is set before calling BinaryFormatter.Deserialize\ndotnet_diagnostic.CA2302.severity = none\n\n# CA2305: Do not use insecure deserializer LosFormatter\ndotnet_diagnostic.CA2305.severity = none\n\n# CA2310: Do not use insecure deserializer NetDataContractSerializer\ndotnet_diagnostic.CA2310.severity = none\n\n# CA2311: Do not deserialize without first setting NetDataContractSerializer.Binder\ndotnet_diagnostic.CA2311.severity = none\n\n# CA2312: Ensure NetDataContractSerializer.Binder is set before deserializing\ndotnet_diagnostic.CA2312.severity = none\n\n# CA2315: Do not use insecure deserializer ObjectStateFormatter\ndotnet_diagnostic.CA2315.severity = none\n\n# CA2321: Do not deserialize with JavaScriptSerializer using a SimpleTypeResolver\ndotnet_diagnostic.CA2321.severity = none\n\n# CA2322: Ensure JavaScriptSerializer is not initialized with SimpleTypeResolver before deserializing\ndotnet_diagnostic.CA2322.severity = none\n\n# CA2326: Do not use TypeNameHandling values other than None\ndotnet_diagnostic.CA2326.severity = none\n\n# CA2327: Do not use insecure JsonSerializerSettings\ndotnet_diagnostic.CA2327.severity = none\n\n# CA2328: Ensure that JsonSerializerSettings are secure\ndotnet_diagnostic.CA2328.severity = none\n\n# CA2329: Do not deserialize with JsonSerializer using an insecure configuration\ndotnet_diagnostic.CA2329.severity = none\n\n# CA2330: Ensure that JsonSerializer has a secure configuration when deserializing\ndotnet_diagnostic.CA2330.severity = none\n\n# CA2350: Do not use DataTable.ReadXml() with untrusted data\ndotnet_diagnostic.CA2350.severity = none\n\n# CA2351: Do not use DataSet.ReadXml() with untrusted data\ndotnet_diagnostic.CA2351.severity = none\n\n# CA2352: Unsafe DataSet or DataTable in serializable type can be vulnerable to remote code execution attacks\ndotnet_diagnostic.CA2352.severity = none\n\n# CA2353: Unsafe DataSet or DataTable in serializable type\ndotnet_diagnostic.CA2353.severity = none\n\n# CA2354: Unsafe DataSet or DataTable in deserialized object graph can be vulnerable to remote code execution attacks\ndotnet_diagnostic.CA2354.severity = none\n\n# CA2355: Unsafe DataSet or DataTable type found in deserializable object graph\ndotnet_diagnostic.CA2355.severity = none\n\n# CA2356: Unsafe DataSet or DataTable type in web deserializable object graph\ndotnet_diagnostic.CA2356.severity = none\n\n# CA2361: Ensure auto-generated class containing DataSet.ReadXml() is not used with untrusted data\ndotnet_diagnostic.CA2361.severity = none\n\n# CA2362: Unsafe DataSet or DataTable in auto-generated serializable type can be vulnerable to remote code execution attacks\ndotnet_diagnostic.CA2362.severity = none\n\n# CA3001: Review code for SQL injection vulnerabilities\ndotnet_diagnostic.CA3001.severity = none\n\n# CA3002: Review code for XSS vulnerabilities\ndotnet_diagnostic.CA3002.severity = none\n\n# CA3003: Review code for file path injection vulnerabilities\ndotnet_diagnostic.CA3003.severity = none\n\n# CA3004: Review code for information disclosure vulnerabilities\ndotnet_diagnostic.CA3004.severity = none\n\n# CA3005: Review code for LDAP injection vulnerabilities\ndotnet_diagnostic.CA3005.severity = none\n\n# CA3006: Review code for process command injection vulnerabilities\ndotnet_diagnostic.CA3006.severity = none\n\n# CA3007: Review code for open redirect vulnerabilities\ndotnet_diagnostic.CA3007.severity = none\n\n# CA3008: Review code for XPath injection vulnerabilities\ndotnet_diagnostic.CA3008.severity = none\n\n# CA3009: Review code for XML injection vulnerabilities\ndotnet_diagnostic.CA3009.severity = none\n\n# CA3010: Review code for XAML injection vulnerabilities\ndotnet_diagnostic.CA3010.severity = none\n\n# CA3011: Review code for DLL injection vulnerabilities\ndotnet_diagnostic.CA3011.severity = none\n\n# CA3012: Review code for regex injection vulnerabilities\ndotnet_diagnostic.CA3012.severity = none\n\n# CA3061: Do Not Add Schema By URL\ndotnet_diagnostic.CA3061.severity = warning\n\n# CA3075: Insecure DTD processing in XML\ndotnet_diagnostic.CA3075.severity = warning\n\n# CA3076: Insecure XSLT script processing.\ndotnet_diagnostic.CA3076.severity = warning\n\n# CA3077: Insecure Processing in API Design, XmlDocument and XmlTextReader\ndotnet_diagnostic.CA3077.severity = warning\n\n# CA3147: Mark Verb Handlers With Validate Antiforgery Token\ndotnet_diagnostic.CA3147.severity = warning\n\n# CA5350: Do Not Use Weak Cryptographic Algorithms\ndotnet_diagnostic.CA5350.severity = warning\n\n# CA5351: Do Not Use Broken Cryptographic Algorithms\ndotnet_diagnostic.CA5351.severity = warning\n\n# CA5358: Review cipher mode usage with cryptography experts\ndotnet_diagnostic.CA5358.severity = none\n\n# CA5359: Do Not Disable Certificate Validation\ndotnet_diagnostic.CA5359.severity = warning\n\n# CA5360: Do Not Call Dangerous Methods In Deserialization\ndotnet_diagnostic.CA5360.severity = warning\n\n# CA5361: Do Not Disable SChannel Use of Strong Crypto\ndotnet_diagnostic.CA5361.severity = warning\n\n# CA5362: Potential reference cycle in deserialized object graph\ndotnet_diagnostic.CA5362.severity = none\n\n# CA5363: Do Not Disable Request Validation\ndotnet_diagnostic.CA5363.severity = warning\n\n# CA5364: Do Not Use Deprecated Security Protocols\ndotnet_diagnostic.CA5364.severity = warning\n\n# CA5365: Do Not Disable HTTP Header Checking\ndotnet_diagnostic.CA5365.severity = warning\n\n# CA5366: Use XmlReader for 'DataSet.ReadXml()'\ndotnet_diagnostic.CA5366.severity = none\n\n# CA5367: Do Not Serialize Types With Pointer Fields\ndotnet_diagnostic.CA5367.severity = none\n\n# CA5368: Set ViewStateUserKey For Classes Derived From Page\ndotnet_diagnostic.CA5368.severity = warning\n\n# CA5369: Use XmlReader for 'XmlSerializer.Deserialize()'\ndotnet_diagnostic.CA5369.severity = none\n\n# CA5370: Use XmlReader for XmlValidatingReader constructor\ndotnet_diagnostic.CA5370.severity = warning\n\n# CA5371: Use XmlReader for 'XmlSchema.Read()'\ndotnet_diagnostic.CA5371.severity = none\n\n# CA5372: Use XmlReader for XPathDocument constructor\ndotnet_diagnostic.CA5372.severity = none\n\n# CA5373: Do not use obsolete key derivation function\ndotnet_diagnostic.CA5373.severity = warning\n\n# CA5374: Do Not Use XslTransform\ndotnet_diagnostic.CA5374.severity = warning\n\n# CA5375: Do Not Use Account Shared Access Signature\ndotnet_diagnostic.CA5375.severity = none\n\n# CA5376: Use SharedAccessProtocol HttpsOnly\ndotnet_diagnostic.CA5376.severity = warning\n\n# CA5377: Use Container Level Access Policy\ndotnet_diagnostic.CA5377.severity = warning\n\n# CA5378: Do not disable ServicePointManagerSecurityProtocols\ndotnet_diagnostic.CA5378.severity = warning\n\n# CA5379: Ensure Key Derivation Function algorithm is sufficiently strong\ndotnet_diagnostic.CA5379.severity = warning\n\n# CA5380: Do Not Add Certificates To Root Store\ndotnet_diagnostic.CA5380.severity = warning\n\n# CA5381: Ensure Certificates Are Not Added To Root Store\ndotnet_diagnostic.CA5381.severity = warning\n\n# CA5382: Use Secure Cookies In ASP.NET Core\ndotnet_diagnostic.CA5382.severity = none\n\n# CA5383: Ensure Use Secure Cookies In ASP.NET Core\ndotnet_diagnostic.CA5383.severity = none\n\n# CA5384: Do Not Use Digital Signature Algorithm (DSA)\ndotnet_diagnostic.CA5384.severity = warning\n\n# CA5385: Use Rivest-Shamir-Adleman (RSA) Algorithm With Sufficient Key Size\ndotnet_diagnostic.CA5385.severity = warning\n\n# CA5386: Avoid hardcoding SecurityProtocolType value\ndotnet_diagnostic.CA5386.severity = none\n\n# CA5387: Do Not Use Weak Key Derivation Function With Insufficient Iteration Count\ndotnet_diagnostic.CA5387.severity = none\n\n# CA5388: Ensure Sufficient Iteration Count When Using Weak Key Derivation Function\ndotnet_diagnostic.CA5388.severity = none\n\n# CA5389: Do Not Add Archive Item's Path To The Target File System Path\ndotnet_diagnostic.CA5389.severity = none\n\n# CA5390: Do not hard-code encryption key\ndotnet_diagnostic.CA5390.severity = none\n\n# CA5391: Use antiforgery tokens in ASP.NET Core MVC controllers\ndotnet_diagnostic.CA5391.severity = none\n\n# CA5392: Use DefaultDllImportSearchPaths attribute for P/Invokes\ndotnet_diagnostic.CA5392.severity = none\n\n# CA5393: Do not use unsafe DllImportSearchPath value\ndotnet_diagnostic.CA5393.severity = none\n\n# CA5394: Do not use insecure randomness\ndotnet_diagnostic.CA5394.severity = none\n\n# CA5395: Miss HttpVerb attribute for action methods\ndotnet_diagnostic.CA5395.severity = none\n\n# CA5396: Set HttpOnly to true for HttpCookie\ndotnet_diagnostic.CA5396.severity = none\n\n# CA5397: Do not use deprecated SslProtocols values\ndotnet_diagnostic.CA5397.severity = none\n\n# CA5398: Avoid hardcoded SslProtocols values\ndotnet_diagnostic.CA5398.severity = none\n\n# CA5399: HttpClients should enable certificate revocation list checks\ndotnet_diagnostic.CA5399.severity = none\n\n# CA5400: Ensure HttpClient certificate revocation list check is not disabled\ndotnet_diagnostic.CA5400.severity = none\n\n# CA5401: Do not use CreateEncryptor with non-default IV\ndotnet_diagnostic.CA5401.severity = none\n\n# CA5402: Use CreateEncryptor with the default IV\ndotnet_diagnostic.CA5402.severity = none\n\n# CA5403: Do not hard-code certificate\ndotnet_diagnostic.CA5403.severity = none\n\n# CA5404: Do not disable token validation checks\ndotnet_diagnostic.CA5404.severity = none\n\n# CA5405: Do not always skip token validation in delegates\ndotnet_diagnostic.CA5405.severity = none\n\n# IL3000: Avoid using accessing Assembly file path when publishing as a single-file\ndotnet_diagnostic.IL3000.severity = warning\n\n# IL3001: Avoid using accessing Assembly file path when publishing as a single-file\ndotnet_diagnostic.IL3001.severity = warning\n\n# IL3002: Using member with RequiresAssemblyFilesAttribute can break functionality when embedded in a single-file app\ndotnet_diagnostic.IL3002.severity = warning\n\n# SA0001: XML comments\ndotnet_diagnostic.SA0001.severity = none\n\n# SA1000: Spacing around keywords\n# suggestion until https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3478 is resolved\ndotnet_diagnostic.SA1000.severity = suggestion\n\n# SA1001: Commas should not be preceded by whitespace\ndotnet_diagnostic.SA1001.severity = warning\n\n# SA1002: Semicolons should not be preceded by a space\ndotnet_diagnostic.SA1002.severity = none\n\n# SA1003: Operator should not appear at the end of a line\ndotnet_diagnostic.SA1003.severity = none\n\n# SA1004: Documentation line should begin with a space\ndotnet_diagnostic.SA1004.severity = none\n\n# SA1005: Single line comment should begin with a space\ndotnet_diagnostic.SA1005.severity = none\n\n# SA1008: Opening parenthesis should not be preceded by a space\ndotnet_diagnostic.SA1008.severity = none\n\n# SA1009: Closing parenthesis should not be followed by a space\ndotnet_diagnostic.SA1009.severity = none\n\n# SA1010: Opening square brackets should not be preceded by a space\ndotnet_diagnostic.SA1010.severity = none\n\n# SA1011: Closing square bracket should be followed by a space\ndotnet_diagnostic.SA1011.severity = none\n\n# SA1012: Opening brace should be followed by a space\ndotnet_diagnostic.SA1012.severity = none\n\n# SA1013: Closing brace should be preceded by a space\ndotnet_diagnostic.SA1013.severity = none\n\n# SA1014: Opening generic brackets should not be preceded by a space\ndotnet_diagnostic.SA1014.severity = warning\n\n# SA1015: Closing generic bracket should not be followed by a space\ndotnet_diagnostic.SA1015.severity = none\n\n# SA1018: Nullable type symbol should not be preceded by a space\ndotnet_diagnostic.SA1018.severity = warning\n\n# SA1020: Increment symbol should not be preceded by a space\ndotnet_diagnostic.SA1020.severity = warning\n\n# SA1021: Negative sign should be preceded by a space\ndotnet_diagnostic.SA1021.severity = none\n\n# SA1023: Dereference symbol '*' should not be preceded by a space.\"\ndotnet_diagnostic.SA1023.severity = none\n\n# SA1024: Colon should be followed by a space\ndotnet_diagnostic.SA1024.severity = none\n\n# SA1025: Code should not contain multiple whitespace characters in a row\ndotnet_diagnostic.SA1025.severity = none\n\n# SA1026: Keyword followed by span or blank line\ndotnet_diagnostic.SA1026.severity = warning\n\n# SA1027: Tabs and spaces should be used correctly\ndotnet_diagnostic.SA1027.severity = warning\n\n# SA1028: Code should not contain trailing whitespace\ndotnet_diagnostic.SA1028.severity = warning\n\n# SA1100: Do not prefix calls with base unless local implementation exists\ndotnet_diagnostic.SA1100.severity = none\n\n# SA1101: Prefix local calls with this\ndotnet_diagnostic.SA1101.severity = none\n\n# SA1102: Query clause should follow previous clause\ndotnet_diagnostic.SA1102.severity = warning\n\n# SA1105: Query clauses spanning multiple lines should begin on own line\ndotnet_diagnostic.SA1105.severity = warning\n\n# SA1106: Code should not contain empty statements\ndotnet_diagnostic.SA1106.severity = none\n\n# SA1107: Code should not contain multiple statements on one line\ndotnet_diagnostic.SA1107.severity = none\n\n# SA1108: Block statements should not contain embedded comments\ndotnet_diagnostic.SA1108.severity = none\n\n# SA1110: Opening parenthesis or bracket should be on declaration line\ndotnet_diagnostic.SA1110.severity = none\n\n# SA1111: Closing parenthesis should be on line of last parameter\ndotnet_diagnostic.SA1111.severity = none\n\n# SA1113: Comma should be on the same line as previous parameter\ndotnet_diagnostic.SA1113.severity = warning\n\n# SA1114: Parameter list should follow declaration\ndotnet_diagnostic.SA1114.severity = none\n\n# SA1115: Parameter should begin on the line after the previous parameter\ndotnet_diagnostic.SA1115.severity = warning\n\n# SA1116: Split parameters should start on line after declaration\ndotnet_diagnostic.SA1116.severity = none\n\n# SA1117: Parameters should be on same line or separate lines\ndotnet_diagnostic.SA1117.severity = none\n\n# SA1118: Parameter should not span multiple lines\ndotnet_diagnostic.SA1118.severity = none\n\n# SA1119: Statement should not use unnecessary parenthesis\ndotnet_diagnostic.SA1119.severity = none\n\n# SA1120: Comments should contain text\ndotnet_diagnostic.SA1120.severity = none\n\n# SA1121: Use built-in type alias\ndotnet_diagnostic.SA1121.severity = warning\n\n# SA1122: Use string.Empty for empty strings\ndotnet_diagnostic.SA1122.severity = none\n\n# SA1123: Region should not be located within a code element\ndotnet_diagnostic.SA1123.severity = none\n\n# SA1124: Do not use regions\ndotnet_diagnostic.SA1124.severity = none\n\n# SA1125: Use shorthand for nullable types\ndotnet_diagnostic.SA1125.severity = none\n\n# SA1127: Generic type constraints should be on their own line\ndotnet_diagnostic.SA1127.severity = none\n\n# SA1128: Put constructor initializers on their own line\ndotnet_diagnostic.SA1128.severity = none\n\n# SA1129: Do not use default value type constructor\ndotnet_diagnostic.SA1129.severity = warning\n\n# SA1130: Use lambda syntax\ndotnet_diagnostic.SA1130.severity = none\n\n# SA1131: Constant values should appear on the right-hand side of comparisons\ndotnet_diagnostic.SA1131.severity = none\n\n# SA1132: Do not combine fields\ndotnet_diagnostic.SA1132.severity = none\n\n# SA1133: Do not combine attributes\ndotnet_diagnostic.SA1133.severity = none\n\n# SA1134: Each attribute should be placed on its own line of code\ndotnet_diagnostic.SA1134.severity = none\n\n# SA1135: Using directive should be qualified\ndotnet_diagnostic.SA1135.severity = none\n\n# SA1136: Enum values should be on separate lines\ndotnet_diagnostic.SA1136.severity = none\n\n# SA1137: Elements should have the same indentation\ndotnet_diagnostic.SA1137.severity = none\n\n# SA1139: Use literal suffix notation instead of casting\ndotnet_diagnostic.SA1139.severity = none\n\n# SA1141: Use tuple syntax\ndotnet_diagnostic.SA1141.severity = warning\n\n# SA1142: Refer to tuple elements by name\ndotnet_diagnostic.SA1142.severity = warning\n\n# SA1200: Using directive should appear within a namespace declaration\ndotnet_diagnostic.SA1200.severity = none\n\n# SA1201: Elements should appear in the correct order\ndotnet_diagnostic.SA1201.severity = none\n\n# SA1202: Elements should be ordered by access\ndotnet_diagnostic.SA1202.severity = none\n\n# SA1203: Constants should appear before fields\ndotnet_diagnostic.SA1203.severity = none\n\n# SA1204: Static elements should appear before instance elements\ndotnet_diagnostic.SA1204.severity = none\n\n# SA1205: Partial elements should declare an access modifier\ndotnet_diagnostic.SA1205.severity = warning\n\n# SA1206: Keyword ordering - TODO Re-enable as warning after https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3527\ndotnet_diagnostic.SA1206.severity = suggestion\n\n# SA1208: Using directive ordering\ndotnet_diagnostic.SA1208.severity = none\n\n# SA1209: Using alias directives should be placed after all using namespace directives\ndotnet_diagnostic.SA1209.severity = none\n\n# SA1210: Using directives should be ordered alphabetically by the namespaces\ndotnet_diagnostic.SA1210.severity = none\n\n# SA1211: Using alias directive ordering\ndotnet_diagnostic.SA1211.severity = none\n\n# SA1212: A get accessor appears after a set accessor within a property or indexer\ndotnet_diagnostic.SA1212.severity = warning\n\n# SA1214: Readonly fields should appear before non-readonly fields\ndotnet_diagnostic.SA1214.severity = none\n\n# SA1216: Using static directives should be placed at the correct location\ndotnet_diagnostic.SA1216.severity = none\n\n# SA1300: Element should begin with an uppercase letter\ndotnet_diagnostic.SA1300.severity = none\n\n# SA1302: Interface names should begin with I\ndotnet_diagnostic.SA1302.severity = warning\n\n# SA1303: Const field names should begin with upper-case letter\ndotnet_diagnostic.SA1303.severity = none\n\n# SA1304: Non-private readonly fields should begin with upper-case letter\ndotnet_diagnostic.SA1304.severity = none\n\n# SA1306: Field should begin with lower-case letter\ndotnet_diagnostic.SA1306.severity = none\n\n# SA1307: Field should begin with upper-case letter\ndotnet_diagnostic.SA1307.severity = none\n\n# SA1308: Field should not begin with the prefix 's_'\ndotnet_diagnostic.SA1308.severity = none\n\n# SA1309: Field names should not begin with underscore\ndotnet_diagnostic.SA1309.severity = none\n\n# SA1310: Field should not contain an underscore\ndotnet_diagnostic.SA1310.severity = none\n\n# SA1311: Static readonly fields should begin with upper-case letter\ndotnet_diagnostic.SA1311.severity = none\n\n# SA1312: Variable should begin with lower-case letter\ndotnet_diagnostic.SA1312.severity = none\n\n# SA1313: Parameter should begin with lower-case letter\ndotnet_diagnostic.SA1313.severity = none\n\n# SA1314: Type parameter names should begin with T\ndotnet_diagnostic.SA1314.severity = none\n\n# SA1316: Tuple element names should use correct casing\ndotnet_diagnostic.SA1316.severity = none\n\n# SA1400: Member should declare an access modifer\ndotnet_diagnostic.SA1400.severity = warning\n\n# SA1401: Fields should be private\ndotnet_diagnostic.SA1401.severity = none\n\n# SA1402: File may only contain a single type\ndotnet_diagnostic.SA1402.severity = none\n\n# SA1403: File may only contain a single namespace\ndotnet_diagnostic.SA1403.severity = none\n\n# SA1404: Code analysis suppression should have justification\ndotnet_diagnostic.SA1404.severity = warning\n\n# SA1405: Debug.Assert should provide message text\ndotnet_diagnostic.SA1405.severity = none\n\n# SA1407: Arithmetic expressions should declare precedence\ndotnet_diagnostic.SA1407.severity = none\n\n# SA1408: Conditional expressions should declare precedence\ndotnet_diagnostic.SA1408.severity = none\n\n# SA1410: Remove delegate parens when possible\ndotnet_diagnostic.SA1410.severity = warning\n\n# SA1411: Attribute constructor shouldn't use unnecessary parenthesis\ndotnet_diagnostic.SA1411.severity = warning\n\n# SA1413: Use trailing comma in multi-line initializers\ndotnet_diagnostic.SA1413.severity = none\n\n# SA1414: Tuple types in signatures should have element names\ndotnet_diagnostic.SA1414.severity = none\n\n# SA1500: Braces for multi-line statements should not share line\ndotnet_diagnostic.SA1500.severity = none\n\n# SA1501: Statement should not be on a single line\ndotnet_diagnostic.SA1501.severity = none\n\n# SA1502: Element should not be on a single line\ndotnet_diagnostic.SA1502.severity = none\n\n# SA1503: Braces should not be omitted\ndotnet_diagnostic.SA1503.severity = none\n\n# SA1504: All accessors should be single-line or multi-line\ndotnet_diagnostic.SA1504.severity = none\n\n# SA1505: An opening brace should not be followed by a blank line\ndotnet_diagnostic.SA1505.severity = none\n\n# SA1506: Element documentation headers should not be followed by blank line\ndotnet_diagnostic.SA1506.severity = none\n\n# SA1507: Code should not contain multiple blank lines in a row\ndotnet_diagnostic.SA1507.severity = none\n\n# SA1508: A closing brace should not be preceded by a blank line\ndotnet_diagnostic.SA1508.severity = none\n\n# SA1509: Opening braces should not be preceded by blank line\ndotnet_diagnostic.SA1509.severity = none\n\n# SA1510: 'else' statement should not be preceded by a blank line\ndotnet_diagnostic.SA1510.severity = none\n\n# SA1512: Single-line comments should not be followed by blank line\ndotnet_diagnostic.SA1512.severity = none\n\n# SA1513: Closing brace should be followed by blank line\ndotnet_diagnostic.SA1513.severity = none\n\n# SA1514: Element documentation header should be preceded by blank line\ndotnet_diagnostic.SA1514.severity = none\n\n# SA1515: Single-line comment should be preceded by blank line\ndotnet_diagnostic.SA1515.severity = none\n\n# SA1516: Elements should be separated by blank line\ndotnet_diagnostic.SA1516.severity = none\n\n# SA1517: Code should not contain blank lines at start of file\ndotnet_diagnostic.SA1517.severity = warning\n\n# SA1518: Code should not contain blank lines at the end of the file\ndotnet_diagnostic.SA1518.severity = warning\n\n# SA1519: Braces should not be omitted from multi-line child statement\ndotnet_diagnostic.SA1519.severity = none\n\n# SA1520: Use braces consistently\ndotnet_diagnostic.SA1520.severity = none\n\n# SA1600: Elements should be documented\ndotnet_diagnostic.SA1600.severity = none\n\n# SA1601: Partial elements should be documented\ndotnet_diagnostic.SA1601.severity = none\n\n# SA1602: Enumeration items should be documented\ndotnet_diagnostic.SA1602.severity = none\n\n# SA1604: Element documentation should have summary\ndotnet_diagnostic.SA1604.severity = none\n\n# SA1605: Partial element documentation should have summary\ndotnet_diagnostic.SA1605.severity = none\n\n# SA1606: Element documentation should have summary text\ndotnet_diagnostic.SA1606.severity = none\n\n# SA1608: Element documentation should not have default summary\ndotnet_diagnostic.SA1608.severity = none\n\n# SA1610: Property documentation should have value text\ndotnet_diagnostic.SA1610.severity = none\n\n# SA1611: The documentation for parameter 'message' is missing\ndotnet_diagnostic.SA1611.severity = none\n\n# SA1612: The parameter documentation is at incorrect position\ndotnet_diagnostic.SA1612.severity = none\n\n# SA1614: Element parameter documentation should have text\ndotnet_diagnostic.SA1614.severity = none\n\n# SA1615: Element return value should be documented\ndotnet_diagnostic.SA1615.severity = none\n\n# SA1616: Element return value documentation should have text\ndotnet_diagnostic.SA1616.severity = none\n\n# SA1618: The documentation for type parameter is missing\ndotnet_diagnostic.SA1618.severity = none\n\n# SA1619: The documentation for type parameter is missing\ndotnet_diagnostic.SA1619.severity = none\n\n# SA1622: Generic type parameter documentation should have text\ndotnet_diagnostic.SA1622.severity = none\n\n# SA1623: Property documentation text\ndotnet_diagnostic.SA1623.severity = none\n\n# SA1624: Because the property only contains a visible get accessor, the documentation summary text should begin with 'Gets'\ndotnet_diagnostic.SA1624.severity = none\n\n# SA1625: Element documentation should not be copied and pasted\ndotnet_diagnostic.SA1625.severity = none\n\n# SA1626: Single-line comments should not use documentation style slashes\ndotnet_diagnostic.SA1626.severity = none\n\n# SA1627: The documentation text within the \\'exception\\' tag should not be empty\ndotnet_diagnostic.SA1627.severity = none\n\n# SA1629: Documentation text should end with a period\ndotnet_diagnostic.SA1629.severity = none\n\n# SA1633: File should have header\ndotnet_diagnostic.SA1633.severity = none\n\n# SA1642: Constructor summary documentation should begin with standard text\ndotnet_diagnostic.SA1642.severity = none\n\n# SA1643: Destructor summary documentation should begin with standard text\ndotnet_diagnostic.SA1643.severity = none\n\n# SA1649: File name should match first type name\ndotnet_diagnostic.SA1649.severity = none\n\n# IDE0001: Simplify name\ndotnet_diagnostic.IDE0001.severity = suggestion\n\n# IDE0002: Simplify member access\ndotnet_diagnostic.IDE0002.severity = suggestion\n\n# IDE0003: Remove this or Me qualification\ndotnet_diagnostic.IDE0003.severity = suggestion\n\n# IDE0004: Remove Unnecessary Cast\ndotnet_diagnostic.IDE0004.severity = suggestion\n\n# IDE0005: Using directive is unnecessary.\ndotnet_diagnostic.IDE0005.severity = warning\n\n# IDE0007: Use implicit type\ndotnet_diagnostic.IDE0007.severity = silent\n\n# IDE0008: Use explicit type\ndotnet_diagnostic.IDE0008.severity = suggestion\n\n# IDE0009: Add this or Me qualification\ndotnet_diagnostic.IDE0009.severity = silent\n\n# IDE0010: Add missing cases\ndotnet_diagnostic.IDE0010.severity = silent\n\n# IDE0011: Add braces\ndotnet_diagnostic.IDE0011.severity = silent\n\n# IDE0016: Use 'throw' expression\ndotnet_diagnostic.IDE0016.severity = silent\n\n# IDE0017: Simplify object initialization\ndotnet_diagnostic.IDE0017.severity = suggestion\n\n# IDE0018: Inline variable declaration\ndotnet_diagnostic.IDE0018.severity = suggestion\n\n# IDE0019: Use pattern matching to avoid as followed by a null check\ndotnet_diagnostic.IDE0019.severity = suggestion\n\n# IDE0020: Use pattern matching to avoid is check followed by a cast (with variable)\ndotnet_diagnostic.IDE0020.severity = warning\n\n# IDE0021: Use expression body for constructors\ndotnet_diagnostic.IDE0021.severity = silent\n\n# IDE0022: Use expression body for methods\ndotnet_diagnostic.IDE0022.severity = silent\n\n# IDE0023: Use expression body for operators\ndotnet_diagnostic.IDE0023.severity = silent\n\n# IDE0024: Use expression body for operators\ndotnet_diagnostic.IDE0024.severity = silent\n\n# IDE0025: Use expression body for properties\ndotnet_diagnostic.IDE0025.severity = silent\n\n# IDE0026: Use expression body for indexers\ndotnet_diagnostic.IDE0026.severity = silent\n\n# IDE0027: Use expression body for accessors\ndotnet_diagnostic.IDE0027.severity = silent\n\n# IDE0028: Simplify collection initialization\ndotnet_diagnostic.IDE0028.severity = suggestion\n\n# IDE0029: Use coalesce expression\ndotnet_diagnostic.IDE0029.severity = warning\n\n# IDE0030: Use coalesce expression\ndotnet_diagnostic.IDE0030.severity = warning\n\n# IDE0031: Use null propagation\ndotnet_diagnostic.IDE0031.severity = warning\n\n# IDE0032: Use auto property\ndotnet_diagnostic.IDE0032.severity = silent\n\n# IDE0033: Use explicitly provided tuple name\ndotnet_diagnostic.IDE0033.severity = suggestion\n\n# IDE0034: Simplify 'default' expression\ndotnet_diagnostic.IDE0034.severity = suggestion\n\n# IDE0035: Remove unreachable code\ndotnet_diagnostic.IDE0035.severity = suggestion\n\n# IDE0036: Order modifiers\ndotnet_diagnostic.IDE0036.severity = warning\n\n# IDE0037: Use inferred member name\ndotnet_diagnostic.IDE0037.severity = silent\n\n# IDE0038: Use pattern matching to avoid is check followed by a cast (without variable)\ndotnet_diagnostic.IDE0038.severity = suggestion\n\n# IDE0039: Use local function\ndotnet_diagnostic.IDE0039.severity = suggestion\n\n# IDE0040: Add accessibility modifiers\ndotnet_diagnostic.IDE0040.severity = suggestion\n\n# IDE0041: Use 'is null' check\ndotnet_diagnostic.IDE0041.severity = warning\n\n# IDE0042: Deconstruct variable declaration\ndotnet_diagnostic.IDE0042.severity = silent\n\n# IDE0043: Invalid format string\ndotnet_diagnostic.IDE0043.severity = warning\n\n# IDE0044: Add readonly modifier\ndotnet_diagnostic.IDE0044.severity = suggestion\n\n# IDE0045: Use conditional expression for assignment\ndotnet_diagnostic.IDE0045.severity = suggestion\n\n# IDE0046: Use conditional expression for return\ndotnet_diagnostic.IDE0046.severity = suggestion\n\n# IDE0047: Remove unnecessary parentheses\ndotnet_diagnostic.IDE0047.severity = silent\n\n# IDE0048: Add parentheses for clarity\ndotnet_diagnostic.IDE0048.severity = silent\n\n# IDE0049: Use language keywords instead of framework type names for type references\ndotnet_diagnostic.IDE0049.severity = warning\n\n# IDE0050: Convert anonymous type to tuple\ndotnet_diagnostic.IDE0050.severity = suggestion\n\n# IDE0051: Remove unused private members\ndotnet_diagnostic.IDE0051.severity = suggestion\n\n# IDE0052: Remove unread private members\ndotnet_diagnostic.IDE0052.severity = suggestion\n\n# IDE0053: Use expression body for lambdas\ndotnet_diagnostic.IDE0053.severity = silent\n\n# IDE0054: Use compound assignment\ndotnet_diagnostic.IDE0054.severity = warning\n\n# IDE0055: Fix formatting\ndotnet_diagnostic.IDE0055.severity = suggestion\n\n# IDE0056: Use index operator\ndotnet_diagnostic.IDE0056.severity = suggestion\n\n# IDE0057: Use range operator\ndotnet_diagnostic.IDE0057.severity = suggestion\n\n# IDE0058: Expression value is never used\ndotnet_diagnostic.IDE0058.severity = silent\n\n# IDE0059: Unnecessary assignment of a value\ndotnet_diagnostic.IDE0059.severity = warning\n\n# IDE0060: Remove unused parameter\ndotnet_diagnostic.IDE0060.severity = silent\ndotnet_code_quality_unused_parameters = non_public\n\n# IDE0061: Use expression body for local functions\ndotnet_diagnostic.IDE0061.severity = silent\n\n# IDE0062: Make local function 'static'\ndotnet_diagnostic.IDE0062.severity = warning\n\n# IDE0063: Use simple 'using' statement\ndotnet_diagnostic.IDE0063.severity = silent\n\n# IDE0064: Make readonly fields writable\ndotnet_diagnostic.IDE0064.severity = silent\n\n# IDE0065: Misplaced using directive\ndotnet_diagnostic.IDE0065.severity = warning\n\n# IDE0066: Convert switch statement to expression\ndotnet_diagnostic.IDE0066.severity = suggestion\n\n# IDE0070: Use 'System.HashCode'\ndotnet_diagnostic.IDE0070.severity = suggestion\n\n# IDE0071: Simplify interpolation\ndotnet_diagnostic.IDE0071.severity = warning\n\n# IDE0072: Add missing cases\ndotnet_diagnostic.IDE0072.severity = silent\n\n# IDE0073: The file header is missing or not located at the top of the file\ndotnet_diagnostic.IDE0073.severity = warning\n\n# IDE0074: Use compound assignment\ndotnet_diagnostic.IDE0074.severity = warning\n\n# IDE0075: Simplify conditional expression\ndotnet_diagnostic.IDE0075.severity = silent\n\n# IDE0076: Invalid global 'SuppressMessageAttribute'\ndotnet_diagnostic.IDE0076.severity = warning\n\n# IDE0077: Avoid legacy format target in 'SuppressMessageAttribute'\ndotnet_diagnostic.IDE0077.severity = silent\n\n# IDE0078: Use pattern matching\ndotnet_diagnostic.IDE0078.severity = suggestion\n\n# IDE0079: Remove unnecessary suppression\ndotnet_diagnostic.IDE0079.severity = suggestion\n\n# IDE0080: Remove unnecessary suppression operator\ndotnet_diagnostic.IDE0080.severity = warning\n\n# IDE0081: Remove unnecessary suppression operator\ndotnet_diagnostic.IDE0081.severity = none\n\n# IDE0082: 'typeof' can be converted  to 'nameof'\ndotnet_diagnostic.IDE0082.severity = warning\n\n# IDE0083: Use pattern matching\ndotnet_diagnostic.IDE0083.severity = silent\n\n# IDE0084: Use pattern matching (IsNot operator)\ndotnet_diagnostic.IDE0084.severity = none\n\n# IDE0090: Use 'new(...)'\ndotnet_diagnostic.IDE0090.severity = silent\n\n# IDE0100: Remove redundant equality\ndotnet_diagnostic.IDE0100.severity = warning\n\n# IDE0110: Remove unnecessary discard\ndotnet_diagnostic.IDE0110.severity = warning\n\n# IDE0120: Simplify LINQ expression\ndotnet_diagnostic.IDE0120.severity = none\n\n# IDE0130: Namespace does not match folder structure\ndotnet_diagnostic.IDE0130.severity = silent\n\n# IDE0140: Simplify object creation\ndotnet_diagnostic.IDE0140.severity = none\n\n# IDE0150: Prefer 'null' check over type check\ndotnet_diagnostic.IDE0150.severity = silent\n\n# IDE0160: Convert to block scoped namespace\ndotnet_diagnostic.IDE0160.severity = silent\n\n# IDE0161: Convert to file-scoped namespace\ndotnet_diagnostic.IDE0161.severity = silent\n\n# IDE1005: Delegate invocation can be simplified.\ndotnet_diagnostic.IDE1005.severity = warning\n\n# IDE1006: Naming styles\ndotnet_diagnostic.IDE1006.severity = silent\n\n# IDE2000: Allow multiple blank lines\ndotnet_diagnostic.IDE2000.severity = silent\n\n# IDE2001: Embedded statements must be on their own line\ndotnet_diagnostic.IDE2001.severity = silent\n\n# IDE2002: Consecutive braces must not have blank line between them\ndotnet_diagnostic.IDE2002.severity = silent\n\n# IDE2003: Allow statement immediately after block\ndotnet_diagnostic.IDE2003.severity = silent\n\n# IDE2004: Blank line not allowed after constructor initializer colon\ndotnet_diagnostic.IDE2004.severity = silent\n"
  },
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n*.sh text eol=lf\n*.ps1 text eol=crlf\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".github/workflows/copilot-setup-steps.yml",
    "content": "name: \"Copilot Setup Steps\"\n\n# Automatically run the setup steps when they are changed to allow for easy validation, and\n# allow manual testing through the repository's \"Actions\" tab\non:\n  workflow_dispatch:\n  push:\n    paths:\n      - .github/workflows/copilot-setup-steps.yml\n  pull_request:\n    paths:\n      - .github/workflows/copilot-setup-steps.yml\n\njobs:\n  # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.\n  copilot-setup-steps:\n    runs-on: ubuntu-latest\n\n    # Set the permissions to the lowest permissions possible needed for your steps.\n    # Copilot will be given its own token for its operations.\n    permissions:\n      # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.\n      contents: read\n\n    # You can define any steps you want, and they will run before the agent starts.\n    # If you do not check out your code, Copilot will do this for you.\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n      - name: Setup .NET SDK\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: |\n            8.0.x\n            9.0.x\n            10.0.x\n            11.0.x\n"
  },
  {
    "path": ".github/workflows/default.yml",
    "content": "name: default\n\non:\n  push:\n    branches:\n      - \"main\"\n      - \"master\"\n      - \"dev\"\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches:\n      - \"main\"\n      - \"master\"\n      - \"dev\"\n\njobs:\n  build:\n    name: Running tests on ${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      # max-parallel: 1\n      matrix:\n        os: [ubuntu-latest, macOS-latest, windows-latest]\n    steps:\n    - uses: actions/checkout@v6\n    - name: Setup .NET SDK\n      uses: actions/setup-dotnet@v5\n      with:\n        dotnet-version: |\n          8.0.x\n          9.0.x\n          10.0.x\n          11.0.x\n    - name: dotnet info\n      run: dotnet --info\n    - name: build\n      run: dotnet build.cs\n"
  },
  {
    "path": ".github/workflows/docfx.yml",
    "content": "name: docs\non:\n  push:\n    branches:\n      - main\n      - master\njobs:\n  build:\n    name: Build\n    runs-on: windows-latest\n    steps:\n      # Check out the branch that triggered this workflow to the 'source' subdirectory\n      - name: Checkout Code\n        uses: actions/checkout@v6\n      - name: Setup .NET SDK\n        uses: actions/setup-dotnet@v5\n        with:\n          dotnet-version: 11.0.x\n      - name: install DocFX\n        run: \"dotnet tool install -g docfx\"\n      - name: Build docs\n        run: \"docfx ./docs/docfx.json\"\n      - name: Deploy\n        uses: peaceiris/actions-gh-pages@v3\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          publish_dir: docs/_site\n"
  },
  {
    "path": ".github/workflows/dotnet-format.yml",
    "content": "name: dotnet-format\n\non:\n  push:\n    branches:\n      - \"dev\"\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n    - name: Setup .NET SDK\n      uses: actions/setup-dotnet@v5\n      with:\n        dotnet-version: 11.0.x\n    - name: build\n      run: dotnet build\n    - name: format\n      run: dotnet format -v=diag\n    - name: check for changes\n      run: |\n        if git diff --exit-code; then\n          echo \"has_changes=false\" >> $GITHUB_ENV\n        else\n          echo \"has_changes=true\" >> $GITHUB_ENV\n        fi\n    - name: Commit and Push\n      if: ${{ env.has_changes == 'true' }}\n      shell: bash\n      run: |\n        # echo $GITHUB_REF_NAME\n        # echo $GITHUB_SHA\n        git config --local user.name \"github-actions[bot]\"\n        git config --local user.email \"weihanli@outlook.com\"\n        git add -u\n        git commit -m \"Automated dotnet-format update from commit ${GITHUB_SHA} on ${GITHUB_REF}\"\n        git log -1\n        remote_repo=\"https://${GITHUB_ACTOR}:${{secrets.GITHUB_TOKEN}}@github.com/${GITHUB_REPOSITORY}.git\"\n        git push \"${remote_repo}\" HEAD:${GITHUB_REF}\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  workflow_dispatch:\n  push:\n    branches: [ master ]\njobs:\n  build:\n    name: Release\n    runs-on: windows-latest\n    steps:\n    - uses: actions/checkout@v6\n    - name: Setup .NET SDK\n      uses: actions/setup-dotnet@v5\n      with:\n        dotnet-version: |\n          8.0.x\n          9.0.x\n          10.0.x\n          11.0.x\n    - name: Build\n      shell: pwsh\n      run: dotnet build.cs --stable=true\n    - name: Get Release Version\n      shell: pwsh\n      run: dotnet ./build/exportReleaseVersion.cs\n    - name: create release\n      shell: pwsh\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      run: |\n        gh release create ${{ env.ReleaseVersion }} --generate-notes --target master\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# Cutom files\nlocalBuild/\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n.mono/\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Oo]ut/\n\n# Visual Studio cache/options directory\n.vs/\n# Visual Studio code options directory\n.vscode/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n**/Properties/launchSettings.json\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Typescript v1 declaration files\ntypings/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush\n.cr/\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\ntools/**\nbuild/tools/**\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n"
  },
  {
    "path": ".husky/commit-msg",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\n# dotnet tool update -g dotnet-execute --prerelease\n# dotnet husky run --name \"commit-message-linter\" --args \"$1\"\n\necho\necho Great work! 🥂\necho\n"
  },
  {
    "path": ".husky/post-push",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\n## husky task runner examples -------------------\n## Note : for local installation use 'dotnet' prefix. e.g. 'dotnet husky'\n\n## run all tasks\n#husky run\n\n### run all tasks with group: 'group-name'\n#husky run --group group-name\n\n## run task with name: 'task-name'\n#husky run --name task-name\n\n## pass hook arguments to task\n#husky run --args \"$1\" \"$2\"\n\n## or put your custom commands -------------------\n#echo 'Husky.Net is awesome!'\n\n# dotnet husky run --group post-push\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\n# task-runner config https://alirezanet.github.io/Husky.Net/guide/task-runner.html\n\n# run tasks by task name\n# dotnet husky run --name \"build\"\n# dotnet husky run --name \"format-verify\"\n\n# run tasks by task group name\n# dotnet husky run --group pre-commit\n"
  },
  {
    "path": ".husky/pre-push",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\n# dotnet husky run --group pre-push\n"
  },
  {
    "path": ".husky/scripts/commit-lint.cs",
    "content": "/*\nA simple regex commit linter\nhttps://www.conventionalcommits.org/en/v1.0.0/\nhttps://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type\n*/\n\nconst string pattern = @\"^(?=.{1,90}$)(?:build|feat|ci|chore|docs|fix|perf|refactor|revert|style|sample|test)(?:\\(.+\\))*(?::).{4,}(?:#\\d+)*(?<![\\.\\s])$\";\n\nvar msg = File.ReadAllLines(args[0])[0];\nConsole.WriteLine(\"Your commit headline message is:\\n> {0}\", msg);\nif (System.Text.RegularExpressions.Regex.IsMatch(msg, pattern))\n    return 0;\n\nif (msg.StartsWith(\"Merge branch \"))\n    return 0;\n\nConsole.ForegroundColor = ConsoleColor.Red;\nConsole.WriteLine(\"Invalid commit message\");\nConsole.ResetColor();\nConsole.WriteLine(\"e.g: 'feat(scope): subject' or 'fix: subject'\");\nConsole.ForegroundColor = ConsoleColor.Gray;\nConsole.WriteLine(\"more info: https://www.conventionalcommits.org/en/v1.0.0/\");\nConsole.ResetColor();\n\nreturn 1;\n"
  },
  {
    "path": ".husky/task-runner.json",
    "content": "{\n  \"tasks\": \n  [\n    {\n      \"name\": \"commit-message-linter\",\n      \"group\": \"commit-msg\",\n      \"command\": \"dotnet-exec\",\n      \"args\": [\".husky/scripts/commit-lint.cs\", \"--args\", \"${args}\"]\n    },    \n    {\n      \"name\": \"test\",\n      \"group\": \"pre-push\",\n      \"command\": \"dotnet\",\n      \"args\": [ \"test\" ],\n      \"exclude\": [\n        \"**/*.md\",\n        \"docs/**/*\"\n      ]\n    },\n    {\n      \"name\": \"dotnet-format\",\n      \"group\": \"post-push\",\n      \"command\": \"dotnet\",\n      \"args\": [ \"format\" ],\n      \"include\": [\n        \"**/*.cs\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# AGENTS.md — WeihanLi.Common\n\nThis file gives AI coding agents the context needed to work on this repository effectively.\n\n## Project Overview\n\n**WeihanLi.Common** is a production-ready .NET utility library that bundles helpers, extensions, and middleware for .NET application development. It covers dependency injection, AOP (Fluent Aspects), eventing, logging, data access helpers, OTP utilities, templating, and more.\n\nThree NuGet packages are published from this repository:\n\n| Package | Description |\n|---|---|\n| `WeihanLi.Common` | Core helpers, extensions, DI, AOP, event bus, TOTP, templating, and more |\n| `WeihanLi.Common.Logging.Serilog` | Integration layer forwarding to Serilog or Microsoft.Extensions.Logging |\n| `WeihanLi.Extensions.Hosting` | Helpers for background services, console apps, and DI bootstrapping |\n\n## Repository Structure\n\n```\n├── src/\n│   ├── WeihanLi.Common/                    # Core library\n│   ├── WeihanLi.Common.Logging.Serilog/    # Serilog integration\n│   └── WeihanLi.Extensions.Hosting/        # Hosting extensions\n├── test/\n│   └── WeihanLi.Common.Test/               # xUnit tests\n├── samples/\n│   ├── AspNetCoreSample/                   # ASP.NET Core usage examples\n│   └── DotNetCoreSample/                   # Console app examples\n├── perf/\n│   └── WeihanLi.Common.Benchmark/         # BenchmarkDotNet benchmarks\n├── docs/                                   # DocFX documentation\n├── build/                                  # Build configuration (signing, versioning)\n├── .github/workflows/                      # GitHub Actions CI/CD\n├── build.cs                                # C# build script (run with dotnet build.cs)\n├── WeihanLi.Common.slnx                   # Solution file (modern .slnx format)\n├── Directory.Build.props                   # Centralized MSBuild configuration\n├── Directory.Packages.props               # Centralized NuGet package versions\n└── global.json                            # .NET SDK version pin\n```\n\n## Build and Test\n\n### Prerequisites\n\n- .NET SDK 10.0+ (see `global.json`; `rollForward` is enabled for newer versions)\n- Supported SDK versions for CI: 8.0.x, 9.0.x, 10.0.x\n\n### Commands\n\n```bash\n# Build the entire solution\ndotnet build\n\n# Run all tests\ndotnet test\n\n# Full CI build (build + test + pack)\ndotnet build.cs\n\n# Format code\ndotnet format\n```\n\n> Set `DISABLE_GITHUB_ACTIONS_TEST_LOGGER=true` to opt out of the GitHub Actions test logger when running tests locally.\n\n### CI\n\n- **GitHub Actions** (`.github/workflows/default.yml`): runs `dotnet build.cs` on Ubuntu, macOS, and Windows\n- **Azure Pipelines** (`azure-pipelines.yml`): additional pipeline for Azure DevOps\n\n## Target Frameworks\n\n- `WeihanLi.Common`: `netstandard2.0`, `net8.0`, `net9.0`, `net10.0`\n- `WeihanLi.Common.Logging.Serilog`: `netstandard2.0`, `net8.0`\n- `WeihanLi.Extensions.Hosting`: `net8.0`\n\nUse conditional compilation (`#if NET8_0_OR_GREATER` etc.) for framework-specific code.\n\n## Key Namespaces and Components\n\n| Namespace | Purpose |\n|---|---|\n| `WeihanLi.Common` | Core utilities, `Guard`, `CacheUtil`, `DependencyResolver` |\n| `WeihanLi.Common.Aspect` | Fluent Aspects AOP — dynamic proxies, interceptors, invocation |\n| `WeihanLi.Common.Data` | ADO.NET extensions, entity mapping, SQL expression parsers |\n| `WeihanLi.Common.DependencyInjection` | Lightweight DI container, service definitions, module support |\n| `WeihanLi.Common.Event` | `EventBus`, `EventQueue`, `EventStore`, publish/subscribe |\n| `WeihanLi.Common.Extensions` | Extension methods for core .NET types |\n| `WeihanLi.Common.Helpers` | `ApplicationHelper`, `TotpHelper`, `CommandExecutor`, `ConsoleHelper`, etc. |\n| `WeihanLi.Common.Http` | HTTP client utilities, mock handlers |\n| `WeihanLi.Common.Logging` | Logging abstractions and adapters |\n| `WeihanLi.Common.Otp` | TOTP/OTP implementation |\n| `WeihanLi.Common.Services` | Common service implementations |\n| `WeihanLi.Common.Template` | Lightweight template engine |\n| `WeihanLi.Extensions` | Shared extension methods (string, collections, etc.) |\n\n## Code Style and Conventions\n\n- **Language**: C# with nullable reference types enabled (`<Nullable>enable</Nullable>`) and implicit usings\n- **Naming**: PascalCase for public members; _camelCase for private fields (private static readonly fields may use PascalCase)\n- **License header**: Apache License 2.0 header in every source file\n- **Formatting**: governed by `.editorconfig` — run `dotnet format` before committing\n\n### Parameter Validation\n\nAlways validate parameters using the `Guard` class:\n\n```csharp\npublic static string Process(string input)\n{\n    Guard.NotNullOrEmpty(input);\n    // implementation\n}\n```\n\n### Extension Methods\n\nPlace extension methods in dedicated files with descriptive names, using the `WeihanLi.Extensions` namespace:\n\n```csharp\nnamespace WeihanLi.Extensions;\n\npublic static class StringExtension\n{\n    public static bool IsNullOrEmpty(this string? str) => string.IsNullOrEmpty(str);\n}\n```\n\n### Fluent API Design\n\nMany components expose fluent interfaces:\n\n```csharp\nFluentAspects.Configure(options =>\n    options.InterceptAll()\n           .With<LoggingInterceptor>()\n);\n```\n\n### Options Pattern\n\nUse the options pattern for configurable components:\n\n```csharp\npublic sealed class ServiceOptions\n{\n    public string ConnectionString { get; set; } = string.Empty;\n    public int Timeout { get; set; } = 30;\n}\n```\n\n## Testing Guidelines\n\n- **Framework**: xUnit v3 on Microsoft Testing Platform (`xunit.v3.mtp-v2`)\n- **Runner**: Microsoft Testing Platform (`UseMicrosoftTestingPlatformRunner=true`)\n- **Location**: `test/WeihanLi.Common.Test/`\n- **File naming**: `{ComponentName}Test.cs`\n- **Namespace**: `WeihanLi.Common.Test`\n- Test method names follow the pattern `MethodName_Scenario_ExpectedResult`\n\n```csharp\n[Fact]\npublic void MethodName_Scenario_ExpectedResult()\n{\n    // Arrange\n    var input = \"test\";\n\n    // Act\n    var result = SystemUnderTest.Process(input);\n\n    // Assert\n    Assert.NotNull(result);\n}\n```\n\nUse `[Theory]` + `[InlineData]` for parameterized tests.\n\n## Common Development Tasks\n\n### Adding a New Extension Method\n\n1. Create (or edit) the appropriate file in `src/WeihanLi.Common/Extensions/`\n2. Use namespace `WeihanLi.Extensions`\n3. Add XML documentation for all public members\n4. Write a corresponding test in `test/WeihanLi.Common.Test/`\n\n### Adding a New Helper or Service\n\n1. Define an interface in `Abstractions/` if applicable\n2. Implement in `Helpers/` or `Services/`\n3. Add configuration options class if configurable\n4. Register with DI if applicable\n5. Write tests and update samples if the feature is significant\n\n### Working with AOP (Fluent Aspects)\n\n```csharp\n// Configure interceptors\nFluentAspects.Configure(options =>\n{\n    options.InterceptMethod<IService>(s => s.Process(Argument.Any<string>()))\n           .With<ValidationInterceptor>();\n});\n\n// Create proxy\nvar service = FluentAspects.AspectOptions.ProxyFactory\n    .CreateProxy<IService>(new ServiceImplementation());\n```\n\n### Database / Data-Access Extensions\n\n```csharp\n// Query using ADO.NET extensions\nvar users = connection.Select<User>(\n    \"SELECT * FROM Users WHERE Age > @age\", new { age = 18 });\n\n// Repository pattern\nvar repository = new Repository<User>(() => connectionFactory.GetConnection());\nvar user = repository.Fetch(u => u.Id == userId);\n```\n\n## Dependencies\n\nKey NuGet dependencies and their roles:\n\n| Package | Role |\n|---|---|\n| `Microsoft.Extensions.*` | Configuration, Logging, Hosting, DI abstractions |\n| `Newtonsoft.Json` | JSON serialization |\n| `Serilog` | Structured logging (via Serilog integration package) |\n| `Dapper` | Lightweight ORM / ADO.NET helper |\n| `PolySharp` | Polyfill attributes for older .NET targets |\n| `xunit.v3` | Test framework |\n| `BenchmarkDotNet` | Performance benchmarks |\n\nPackage versions are centrally managed in `Directory.Packages.props` — update versions there rather than in individual project files.\n\n## Security\n\n- Validate all inputs using `Guard` utilities\n- Dispose resources properly (use `using` statements)\n- Use `RandomNumberGenerator` (not `Random`) for cryptographic operations\n- Never commit secrets or credentials\n- NuGet audit is enabled via MSBuild properties (see `Directory.Build.props` / `Directory.Packages.props` with `<NuGetAudit>true</NuGetAudit>`); do not suppress audit warnings without justification\n\n## Pull Request Guidelines\n\n### Commit Message Conventions\n\nThis project follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. Every commit message must be structured as:\n\n```\n<type>[optional scope]: <description>\n\n[optional body]\n\n[optional footer(s)]\n```\n\n**Common types:**\n\n| Type | When to use |\n|---|---|\n| `feat` | A new feature (correlates with MINOR in SemVer) |\n| `fix` | A bug fix (correlates with PATCH in SemVer) |\n| `docs` | Documentation-only changes |\n| `style` | Formatting, missing semicolons, etc. — no logic change |\n| `refactor` | Code change that is neither a fix nor a feature |\n| `perf` | Performance improvement |\n| `test` | Adding or correcting tests |\n| `build` | Changes to the build system or external dependencies |\n| `ci` | Changes to CI/CD configuration files |\n| `chore` | Maintenance tasks that don't modify src or test files |\n\n**Breaking changes**: append `!` after the type/scope or add a `BREAKING CHANGE:` footer.\n\n**Examples:**\n\n```\nfeat(event): add retry support to EventBus\nfix(otp): correct TOTP window boundary calculation\ndocs: update AGENTS.md with PR guidelines\nbuild: bump Newtonsoft.Json to 13.0.4\nfeat!: remove obsolete IDependencyResolver overloads\n\nBREAKING CHANGE: IDependencyResolver.GetService<T>() overloads that\naccepted a string key have been removed. Use keyed services instead.\n```\n\n### Required Checks Before Submitting\n\n- `dotnet build` — must compile without errors or warnings\n- `dotnet test` — all tests must pass\n- `dotnet format --verify-no-changes` — no formatting violations\n\n### PR Title Format\n\nUse the same `<type>[optional scope]: <description>` format as the commit message.\n\n## Debugging and Troubleshooting\n\n- **Build failures**: run `dotnet build --verbosity detailed` for full MSBuild output\n- **Test failures on a specific framework**: use `dotnet test -f net8.0` to target one TFM\n- **GitHub Actions test logger noise locally**: set `DISABLE_GITHUB_ACTIONS_TEST_LOGGER=true`\n- **NuGet restore errors**: verify `Directory.Packages.props` contains the version; do not add `<Version>` in individual `.csproj` files\n- **Reflection.Emit issues on netstandard2.0**: the package conditionally references `System.Reflection.Emit` — guard with `#if !NETSTANDARD2_0` or `#if NET8_0_OR_GREATER` as appropriate\n\n## Documentation\n\n- XML documentation is required on all public APIs\n- Docs live in `docs/` and are generated with DocFX (`docs/docfx.json`)\n- Release notes are in `docs/ReleaseNotes.md`\n- Samples in `samples/` demonstrate end-to-end usage patterns\n"
  },
  {
    "path": "Directory.Build.props",
    "content": "<Project>\n  <Import Project=\"./build/version.props\" />\n  <PropertyGroup>\n    <LatestTargetFramework>net10.0</LatestTargetFramework>\n    <LangVersion>preview</LangVersion>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n    <Authors>WeihanLi</Authors>\n    <Copyright>Copyright 2017-$([System.DateTime]::Now.Year) (c) WeihanLi</Copyright>\n    <NoWarn>$(NoWarn);NU5048;CS1591;NETSDK1057</NoWarn>\n    <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>\n    <ContinuousIntegrationBuild Condition=\"'$(CI)' == 'true' or '$(TF_BUILD)' == 'true' or '$(GITHUB_ACTIONS)' == 'true'\">true</ContinuousIntegrationBuild>\n    <!-- NuGet Audit https://learn.microsoft.com/en-us/nuget/concepts/auditing-packages -->\n    <NuGetAudit>true</NuGetAudit>\n    <NuGetAuditMode>direct</NuGetAuditMode>\n  </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "Directory.Build.targets",
    "content": "<Project>\n</Project>\n"
  },
  {
    "path": "Directory.Packages.props",
    "content": "<Project>\n  <PropertyGroup>\n    <!-- Enable central package management -->\n    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>\n    <!-- Enable Transitive Package Pinning -->\n    <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>\n    <!-- https://learn.microsoft.com/en-us/nuget/concepts/auditing-packages -->    \n    <NuGetAudit>true</NuGetAudit>    \n    <NuGetAuditMode>all</NuGetAuditMode>\n    <!-- https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1901-nu1904 -->\n    <WarningsAsErrors>NU1901;NU1902;NU1903;NU1904</WarningsAsErrors>\n  </PropertyGroup>\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'net8.0'\">\n    <PackageVersion Include=\"Microsoft.Extensions.Configuration\" Version=\"8.0.0\" />\n    <PackageVersion Include=\"Microsoft.Extensions.Hosting\" Version=\"8.0.1\" />\n    <PackageVersion Include=\"Microsoft.Extensions.Logging\" Version=\"8.0.1\" />\n  </ItemGroup>\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'net9.0'\">\n    <PackageVersion Include=\"Microsoft.Extensions.Configuration\" Version=\"9.0.15\" />\n    <PackageVersion Include=\"Microsoft.Extensions.Hosting\" Version=\"9.0.15\" />\n    <PackageVersion Include=\"Microsoft.Extensions.Logging\" Version=\"9.0.15\" />\n  </ItemGroup>\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'net10.0'\">\n    <PackageVersion Include=\"Microsoft.Extensions.Configuration\" Version=\"10.0.6\" />\n    <PackageVersion Include=\"Microsoft.Extensions.Logging\" Version=\"10.0.6\" />\n  </ItemGroup>\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'net11.0'\">\n    <PackageVersion Include=\"Microsoft.Extensions.Configuration\" Version=\"11.0.0-preview.3.26207.106\" />\n    <PackageVersion Include=\"Microsoft.Extensions.Logging\" Version=\"11.0.0-preview.3.26207.106\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageVersion Include=\"Microsoft.CSharp\" Version=\"4.7.0\" />\n    <PackageVersion Include=\"System.Reflection.Emit\" Version=\"4.7.0\" />\n    <PackageVersion Include=\"System.ComponentModel.Annotations\" Version=\"5.0.0\" />\n    <PackageVersion Include=\"Newtonsoft.Json\" Version=\"13.0.4\" />\n    <PackageVersion Include=\"Serilog\" Version=\"4.3.1\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageVersion Include=\"Moq\" Version=\"4.20.72\" />\n    <PackageVersion Include=\"xunit.v3.mtp-v2\" Version=\"3.2.2\" />\n    <PackageVersion Include=\"GitHubActionsTestLogger\" Version=\"3.0.3\" />\n    <PackageVersion Include=\"Microsoft.Testing.Platform\" Version=\"2.2.1\" />\n    <PackageVersion Include=\"coverlet.collector\" Version=\"10.0.0\" />\n    <PackageVersion Include=\"BenchmarkDotNet\" Version=\"0.15.8\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageVersion Include=\"Microsoft.EntityFrameworkCore\" Version=\"10.0.6\" />\n    <PackageVersion Include=\"Microsoft.EntityFrameworkCore.InMemory\" Version=\"10.0.6\" />\n    <PackageVersion Include=\"Microsoft.Extensions.ObjectPool\" Version=\"10.0.6\" />\n    <PackageVersion Include=\"Serilog.Sinks.Console\" Version=\"6.1.1\" />\n    <PackageVersion Include=\"Microsoft.Data.SqlClient\" Version=\"7.0.0\" />\n    <PackageVersion Include=\"Dapper\" Version=\"2.1.72\" />\n  </ItemGroup>\n  <ItemGroup>\n    <GlobalPackageReference Include=\"PolySharp\" Version=\"1.15.0\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2017 WeihanLi\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# WeihanLi.Common\n\n[![WeihanLi.Common Latest Stable](https://img.shields.io/nuget/v/WeihanLi.Common.svg)](https://www.nuget.org/packages/WeihanLi.Common/)\n[![WeihanLi.Common Latest Preview](https://img.shields.io/nuget/vpre/WeihanLi.Common)](https://www.nuget.org/packages/WeihanLi.Common/absoluteLatest)\n[![Azure Pipelines Build Status](https://weihanli.visualstudio.com/Pipelines/_apis/build/status/WeihanLi.WeihanLi.Common?branchName=master)](https://weihanli.visualstudio.com/Pipelines/_build/latest?definitionId=16&branchName=master)\n[![GitHub Actions Build Status](https://github.com/WeihanLi/WeihanLi.Common/actions/workflows/default.yml/badge.svg)](https://github.com/WeihanLi/WeihanLi.Common/actions/workflows/default.yml)\n\n## Overview\n\n`WeihanLi.Common` bundles a set of production-ready helpers, extensions, and middleware to simplify .NET application development. It covers dependency injection, AOP, eventing, logging, data access helpers, OTP utilities, templating, and more—built from real-world usage and battle-tested across services.\n\n## Packages\n\n| Package | Description |\n| --- | --- |\n| `WeihanLi.Common` | Core helpers and extensions: dependency resolver, configuration helpers, pipelines, TOTP utilities, command executors, etc. |\n| `WeihanLi.Common.Logging.Serilog` | Integration layer so core logging abstractions seamlessly forward to Serilog or Microsoft.Extensions.Logging. |\n| `WeihanLi.Extensions.Hosting` | Hosting helpers for quickly wiring background services, console apps, and DI bootstrapping. |\n\nEach package is available on NuGet and can be referenced independently.\n\n## Installation\n\nInstall the package you need using the .NET CLI or NuGet Package Manager:\n\n```bash\ndotnet add package WeihanLi.Common\n# or install the Serilog integration\ndotnet add package WeihanLi.Common.Logging.Serilog\n```\n\nPreview builds are also published; append `--version <preview>` to opt in.\n\n## Quick Start\n\n```csharp\nusing WeihanLi.Common.Helpers;\n\n// Generate a TOTP code with the built-in helper.\nvar secret = $\"{ApplicationHelper.ApplicationName}_demo_secret\";\nvar code = TotpHelper.GenerateCode(secret);\n\nConsole.WriteLine($\"Generated code: {code}\");\n\nvar isValid = TotpHelper.ValidateCode(secret, code);\nConsole.WriteLine($\"Valid code: {isValid}\");\n```\n\nSee `samples/DotNetCoreSample` and `samples/AspNetCoreSample` for richer scenarios that exercise DI decorators, event buses, logging pipelines, and more.\n\n## Feature Highlights\n\n- **Dependency Injection**: Lightweight container abstractions, decorator helpers, metadata-based registrations, proxy support, and integration with `Microsoft.Extensions.DependencyInjection`.\n- **Fluent Aspects (AOP)**: Dynamic proxy-based interception with fluent configuration for method-level behaviors (logging, validation, caching, etc.).\n- **Event Infrastructure**: In-memory and queue-backed `EventBus`, `EventStore`, and handler helpers to wire publish/subscribe workflows.\n- **Logging**: Abstractions with adapters for built-in logging and Serilog plus formatting helpers to ship structured events.\n- **Data Access Helpers**: Dapper-style extensions over ADO.NET, entity mapping utilities, and SQL expression parsers to streamline data access layers.\n- **OTP & Security**: TOTP implementation, helpers, and service abstractions to secure user flows with minimal setup.\n- **Templating & Text**: Lightweight template engine and string utility helpers for dynamic content rendering.\n- **Utilities**: Process executor, pipeline builder, command helpers, configuration helpers, async invokers, and more.\n\n## Documentation & Samples\n\n- Browse the docs in `docs/index.md` and articles under `docs/articles` for deep dives.\n- API references can be generated with DocFX (`docs/docfx.json`).\n- Samples live under `samples/` and demonstrate step-by-step usage patterns you can adapt.\n\n## Building & Testing\n\n```bash\ndotnet build\ndotnet test\n```\n\nCI builds run with both Azure Pipelines (`azure-pipelines.yml`) and GitHub Actions (`.github/workflows/default.yml`), so you can rely on the same commands locally.\n\n> Set the `DISABLE_GITHUB_ACTIONS_TEST_LOGGER` environment variable to `true` if you need to opt out of the GitHub Actions test logger integration (useful for experimenting with other reporters or diagnosing runner differences).\n\n## Release Notes\n\nHead to the [merged pull requests list](https://github.com/WeihanLi/WeihanLi.Common/pulls?q=is%3Apr+is%3Amerged+base%3Amaster) or the docfx [ReleaseNotes](docs/ReleaseNotes.md) for a chronological view of changes.\n"
  },
  {
    "path": "WeihanLi.Common.sln.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CA/@EntryIndexedValue\">CA</s:String>\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OS/@EntryIndexedValue\">OS</s:String>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Enricher/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=sourcebranchname/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Weihan/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "WeihanLi.Common.slnx",
    "content": "<Solution>\n  <Folder Name=\"/perf/\">\n    <Project Path=\"perf/WeihanLi.Common.Benchmark/WeihanLi.Common.Benchmark.csproj\" />\n  </Folder>\n  <Folder Name=\"/samples/\">\n    <Project Path=\"samples/AspNetCoreSample/AspNetCoreSample.csproj\" />\n    <Project Path=\"samples/DotNetCoreSample/DotNetCoreSample.csproj\" />\n  </Folder>\n  <Folder Name=\"/src/\">\n    <Project Path=\"src/WeihanLi.Common.Logging.Serilog/WeihanLi.Common.Logging.Serilog.csproj\" />\n    <Project Path=\"src/WeihanLi.Common/WeihanLi.Common.csproj\" />\n    <Project Path=\"src/WeihanLi.Extensions.Hosting/WeihanLi.Extensions.Hosting.csproj\" />\n  </Folder>\n  <Folder Name=\"/test/\">\n    <Project Path=\"test/WeihanLi.Common.Test/WeihanLi.Common.Test.csproj\" />\n  </Folder>\n</Solution>\n"
  },
  {
    "path": "azure-pipelines.yml",
    "content": "# https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/pr?view=azure-pipelines\npr: none\n\ntrigger:\n  branches:\n    include:\n    - 'master' # must quote since \"*\" is a YAML reserved character; we want a string\n    - 'main'\n    - 'preview'\n    - 'dev'\n  paths:\n    exclude:\n    - '*.md'\n    - 'docs'\n    - '.github'\n    - '.husky'\n    - '.devcontainer'\n\npool:\n  vmImage: 'windows-latest'\n\nsteps:\n- task: UseDotNet@2\n  displayName: 'Use .NET 8 sdk'\n  inputs:\n    packageType: sdk\n    version: 8.0.x\n- task: UseDotNet@2\n  displayName: 'Use .NET 9 sdk'\n  inputs:\n    packageType: sdk\n    version: 9.0.x\n- task: UseDotNet@2\n  displayName: 'Use .NET 10 sdk'\n  inputs:\n    packageType: sdk\n    version: 10.0.x\n- task: UseDotNet@2\n  displayName: 'Use .NET 11 sdk'\n  inputs:\n    packageType: sdk\n    version: 11.0.x\n    includePreviewVersions: true # Required for preview versions\n\n\n- script: dotnet --info\n  displayName: 'dotnet info'\n\n- script: dotnet build.cs\n  displayName: 'Build Script'\n  env:\n    NuGet__ApiKey: $(nugetApiKey)\n\n# # Publish code coverage results v2\n# # Publish any of the code coverage results from a build.\n# - task: PublishCodeCoverageResults@2\n#   inputs:\n#     summaryFileLocation: \"$(System.DefaultWorkingDirectory)/**/coverage.cobertura.xml\" # string. Required. Path to summary files. \n#     #pathToSources: # string. Path to Source files. \n#     #failIfCoverageEmpty: false # boolean. Fail if code coverage results are missing. Default: false.\n"
  },
  {
    "path": "build/exportReleaseVersion.cs",
    "content": "var currentDirectory = Directory.GetCurrentDirectory();\nConsole.WriteLine($\"currentDirectory: {currentDirectory}\");\n\nvar versionPropsFile = \"./build/version.props\";\nvar doc = System.Xml.Linq.XDocument.Load(versionPropsFile);\nvar propertyGroupNode = doc.Element(\"Project\")?.Element(\"PropertyGroup\");\nArgumentNullException.ThrowIfNull(propertyGroupNode);\n\nvar version = $\"{propertyGroupNode.Element(\"VersionMajor\")!.Value}.{propertyGroupNode.Element(\"VersionMinor\")!.Value}.{propertyGroupNode.Element(\"VersionPatch\")!.Value}\";\nConsole.WriteLine($\"Version: {version}\");\n\nvar envFile = Environment.GetEnvironmentVariable(\"GITHUB_ENV\");\nConsole.WriteLine($\"EnvFilePath: {envFile}\");\n\nif (string.IsNullOrEmpty(envFile)) return;\n\nFile.WriteAllText(envFile, $\"ReleaseVersion={version}\");\nConsole.WriteLine(File.ReadAllText(envFile));\n"
  },
  {
    "path": "build/sign.props",
    "content": "<Project>\n  <PropertyGroup Condition=\"'$(OS)' == 'Windows_NT' and '$(Configuration)' == 'Release'\">\n    <SignAssembly>True</SignAssembly>\n    <AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)weihanli.snk</AssemblyOriginatorKeyFile>\n    <DelaySign>False</DelaySign>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "build/version.props",
    "content": "<Project>\n  <PropertyGroup>\n    <VersionMajor>1</VersionMajor>\n    <VersionMinor>0</VersionMinor>\n    <VersionPatch>88</VersionPatch>\n    <VersionPrefix>$(VersionMajor).$(VersionMinor).$(VersionPatch)</VersionPrefix>\n  </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "build.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license version 2.0 http://www.apache.org/licenses/LICENSE-2.0\n\n#:project ./src/WeihanLi.Common/WeihanLi.Common.csproj\n#:property PublishAot=false\n\nusing WeihanLi.Common.Helpers;\n\nvar solutionPath = \"./WeihanLi.Common.slnx\";\nstring[] srcProjects = [ \n    \"./src/WeihanLi.Common/WeihanLi.Common.csproj\",\n    \"./src/WeihanLi.Common.Logging.Serilog/WeihanLi.Common.Logging.Serilog.csproj\",\n    \"./src/WeihanLi.Extensions.Hosting/WeihanLi.Extensions.Hosting.csproj\",\n];\nstring[] testProjects = [ \"./test/WeihanLi.Common.Test/WeihanLi.Common.Test.csproj\" ];\n\nawait DotNetPackageBuildProcess\n    .Create(options => \n    {\n        options.SolutionPath = solutionPath;\n        options.SrcProjects = srcProjects;\n        options.TestProjects = testProjects;\n    })\n    .ExecuteAsync(args);\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "###############\n#    folder   #\n###############\n/**/DROP/\n/**/TEMP/\n/**/packages/\n/**/bin/\n/**/obj/\n_site\n"
  },
  {
    "path": "docs/ReleaseNotes.md",
    "content": "# Package Release Notes\n\n## WeihanLi.Common\n\nSee pull requests list for changes <https://github.com/WeihanLi/WeihanLi.Common/pulls?q=is%3Apr+is%3Amerged+base%3Amaster>\n\n### [WeihanLi.Common 1.0.50](https://www.nuget.org/packages/WeihanLi.Common/1.0.50)\n\n- Update `HttpClientExtensions` to fix extension conflict with `System.Net.Http.Json`\n- Update `Validator`\n\n### [WeihanLi.Common 1.0.49](https://www.nuget.org/packages/WeihanLi.Common/1.0.49)\n\n- Update `HttpClientExtensions`\n- Add `WrapTask`/`WrapValueTask` support\n- Add `Properties`/`UseWhen` for `PipelineBuilder`\n- Refactor `Result`\n- Better nullable analysis\n- Improve `ValueTask` support\n\n### [WeihanLi.Common 1.0.48](https://www.nuget.org/packages/WeihanLi.Common/1.0.48)\n\n- Fix `ValueTask` Support\n- Add `MockHttpHandler`\n- Remove `JetBrains.Annotations`\n- Refactor on `CommandExecutor`\n- Add `GetRequiredAppSetting` and `GetAppSetting` with default value\n\n### [WeihanLi.Common 1.0.47](https://www.nuget.org/packages/WeihanLi.Common/1.0.47)\n\n- Fix NuGet package warnings\n- Add `GenericLogger`\n- Add `Append[Line]If` with text factory\n- Add `TenantIdProvider`\n- Update `Random` for thread-safe instance\n- Add `ValueAsyncPipelineBuilder`\n- Add feature flags ConfigurationExtension\n- Add .NET 6 target\n- Fix nullable warnings\n\n### [WeihanLi.Common 1.0.46](https://www.nuget.org/packages/WeihanLi.Common/1.0.46)\n\n- `ConsoleLoggingProvider` implement\n- `Dump` extensions\n- update `ValueStopwatch`/`ProfilerHelper`\n- nullable reference types enhancement\n\n### [WeihanLi.Common 1.0.44](https://www.nuget.org/packages/WeihanLi.Common/1.0.44)\n\n- enable nullable reference types\n- add `ValueStopwatch`\n- some enhancements\n\n### [WeihanLi.Common 1.0.43](https://www.nuget.org/packages/WeihanLi.Common/1.0.43)\n\n- refactor `RetryHelper`\n- update `MapHelper`/`UnitOfWork`\n\n### [WeihanLi.Common 1.0.42](https://www.nuget.org/packages/WeihanLi.Common/1.0.42)\n\n- update `HashHelper`/`Exception.Unwrap`\n- update `object.To` extension method, fix bug when nullable type value convert\n- remove `ThreadPrincipalUserIdProvider`(may cause security issue)\n\n### [WeihanLi.Common 1.0.41](https://www.nuget.org/packages/WeihanLi.Common/1.0.41)\n\n- update `ConsoleOutput`\n- update `IRepository`, fix #100\n\n### [WeihanLi.Common 1.0.40](https://www.nuget.org/packages/WeihanLi.Common/1.0.40)\n\n- add `ProcessExecutor`/`CommandRunner`/`ConsoleOutput`\n- add `Base62Encoder`/`Base36Encoder`\n- add `DelegateTextWriter`\n- add `SequentialGuidIdGenerator`\n- update `ResultModel`/`TOTP`/`ProxyUtils`/`AspectCoreExtensions`/`IdGenerator`/ logging extensions\n\n### [WeihanLi.Common 1.0.39](https://www.nuget.org/packages/WeihanLi.Common/1.0.39)\n\n- add `BuildFluentAspectsProvider` extensions\n- add `DelegateInterceptor`\n- add `NoInterceptProperty` extension\n- update `IInterceptorResolver` return IReadOnlyList instead of IReadCollection\n- add arguments for `IProxyFactory.CreateProxyWithTarget`\n- add support for property injection support\n\n### [WeihanLi.Common 1.0.38](https://www.nuget.org/packages/WeihanLi.Common/1.0.38)\n\n- add `IUserIdProvider`/`ICancellationTokenProvider`\n- add `NullEventSubscriptionManager`\n- add biz models, `ReviewRequest` and more ...\n- update `PipelineBuilder` add empty complete delegate as default\n- add `JsonSerializeExtension.SerializerSettingsWith`, optimize `ToEventMsg`/`ToEvent`\n- update `FluentAspect`, allow add optional constructor parameters, export FluentAspectInterceptor for Castle and AspectCore\n\n### [WeihanLi.Common 1.0.37](https://www.nuget.org/packages/WeihanLi.Common/1.0.37)\n\n- refact events related `EventBus`/`EventStore`/`EventQueue`/`EventPublisher`/`EventSubscriber`\n- update `ResultModel`\n- add `ReviewState`/`Categories`/`PagedRequest`/`BaseEntityWithDeleted`/`PagedRequest`\n- add `CastleProxyTypeFactory`/`FluentAspectInterceptorSelector`\n- expose `ActivateHelper` `ObjectFactory`/`FindApplicableConstructor`\n\n### [WeihanLi.Common 1.0.36](https://www.nuget.org/packages/WeihanLi.Common/1.0.36)\n\n- refact `FluentAspect`, add `InvocationEnricher`, fix #75\n- add `IEventQueue`/`DelegateHelper`/`BaseEntity`\n\n### [WeihanLi.Common 1.0.35](https://www.nuget.org/packages/WeihanLi.Common/1.0.35)\n\n- add `FluentAspect` implemented AOP\n- add `TimeoutAfter` extensions\n\n### [WeihanLi.Common 1.0.34](https://www.nuget.org/packages/WeihanLi.Common/1.0.34)\n\n- add `PipelineBuilder` to create pipeline easily\n- update `TotpHelper`, fix bug when the code starts with `0`\n\n### [WeihanLi.Common 1.0.33](https://www.nuget.org/packages/WeihanLi.Common/1.0.33)\n\n- update `ServiceCollectionDependencyResolver` to fix generic scoped service resolve\n- add `ServiceContainerDependencyResolver`\n- update `DataExtension`/`DbConnectionExtension`/`DbCommandExtension` fix #9\n\n### [WeihanLi.Common 1.0.32](https://www.nuget.org/packages/WeihanLi.Common/1.0.32)\n\n- update di extensions, update `GetExportedTypes` with `GetTypes`\n- add interface type filter for `RegisterAssemblyTypesAsImplementedInterfaces`/`RegisterTypeAsImplementedInterfaces`\n- add `ActivatorHelper.CreateInstance<T>(params object[] parameters)`\n- update `TotpHelper`, add check for null salt, disable backward step moving\n\n### [WeihanLi.Common 1.0.31](https://www.nuget.org/packages/WeihanLi.Common/1.0.31)\n\n- update `AsyncLock`, add `Lock`\n- update `DependencyResolver`\n- add `StringHelper.ToPascalCase`/`StringHelper.ToCamelCase`\n- add `UnitOfWork`/`DelegateLoggerProvider`/`TotpOptions`\n- rename `JsonToType` => `JsonToObject`(breaking change)\n\n### [WeihanLi.Common 1.0.30](https://www.nuget.org/packages/WeihanLi.Common/1.0.30)\n\n- update `ExpressionExtension.And`/`ExpressionExtension.Or`\n- add `SqlExpressionVisitor`\n- add `RegisterModule` extension for `IServiceCollection`\n- add extensions for `IServiceContainerBuilder`\n\n### [WeihanLi.Common 1.0.29](https://www.nuget.org/packages/WeihanLi.Common/1.0.29)\n\n- add di extension\n- add `StringExtension.TrimStart(this string str, string start)` extension\n\n### [WeihanLi.Common 1.0.28](https://www.nuget.org/packages/WeihanLi.Common/1.0.28)\n\n- refact logging\n- update `JsonResultModel`/`JsonResultStatus` => `ResultModel`/`ResultStatus`\n- add `NetHelper.IsPrivateIP`/`IPNetwork`\n- add httpHeader parameters for `HttpHelper.HttpPostFile`\n\n### [WeihanLi.Common 1.0.26](https://www.nuget.org/packages/WeihanLi.Common/1.0.26)\n\n- update event, add async support for publish and subscribe/unsubscribe\n- update di, add `ServiceContainerBuilder`/`ServiceContainerModule` to register service\n- update cron, export `CronExpression`\n- add `ArrayHelper.Empty` for net45,update `ConfigurationHelper` for netstandard2.0\n\n### [WeihanLi.Common 1.0.25](https://www.nuget.org/packages/WeihanLi.Common/1.0.25)\n\n- add `ValidateResultModel`\n- update `PagedListModel`\n- add build-in di support\n\n### [WeihanLi.Common 1.0.24.3](https://www.nuget.org/packages/WeihanLi.Common/1.0.24.3)\n\n- add `NetHelper.GetRandomPort`\n- add `TaskHelper.CompletedTask`\n- optimize `ToByteArray(this Stream @this)`/`ToByteArrayAsync(this Stream @this)`\n- add `ExpressionExtension.True<T>`/`ExpressionExtension.False<T>`\n\n### [WeihanLi.Common 1.0.24.2](https://www.nuget.org/packages/WeihanLi.Common/1.0.24.2)\n\n- optimize `CronHelper` and `PagedListModel`\n\n### [WeihanLi.Common 1.0.24](https://www.nuget.org/packages/WeihanLi.Common/1.0.24)\n\n- add logging filter\n- add `Distinct<T>(Func<T, T, bool> compareFunc)` extension\n\n### [WeihanLi.Common 1.0.23.8](https://www.nuget.org/packages/WeihanLi.Common/1.0.23.8)\n\n- add `CronHelper`/`ConcurrentSet`\n- update `EventStoreInMemory`\n- update logging\n\n### [WeihanLi.Common 1.0.23.7](https://www.nuget.org/packages/WeihanLi.Common/1.0.23.7)\n\n- add `PeriodBatching`\n- update `GetValueGetter`/`GetValueSetter`\n\n### [WeihanLi.Common 1.0.23.6](https://www.nuget.org/packages/WeihanLi.Common/1.0.23.6)\n\n- remove `IsEmpty` for `IEventStore`\n- fix `EventStore` `RemoveSubscribtion` bug\n\n### [WeihanLi.Common 1.0.23.4](https://www.nuget.org/packages/WeihanLi.Common/1.0.23.4)\n\n- expose `ApplicationHelper.AppRoot`\n- add `IEventBus`/`IPAddressConverter`/`IPEndPointConverter`/`FuncExtension`\n- update `HttpClientExtensions`/`SecurityHelper`\n\n### [WeihanLi.Common 1.0.22](https://www.nuget.org/packages/WeihanLi.Common/1.0.22)\n\n- add `DateTimeFormatConverter`\n- add `LeftJoin` extension linq method for `IEnumerable`\n- add `ActivatorHelper` to create instance\n- update `Newtonsoft.Json` package version\n- update `JsonResultModel`\n- update `Pagedlistmodel`, fix json serialize bug\n\n### [WeihanLi.Common 1.0.21](https://www.nuget.org/packages/WeihanLi.Common/1.0.21)\n\n- add `CancellationToken` support for Repository and DataExtensions async operations\n- optimize DataExtension remove `new()` LIMIT\n\n### [WeihanLi.Common 1.0.20](https://www.nuget.org/packages/WeihanLi.Common/1.0.20)\n\n- add [sourceLink](https://github.com/dotnet/sourcelink) support\n- add back `SetDependencyResolver(IServiceProvider serviceProvider)` for netstandard2.0\n- remove `System.Configuration.ConfigurationManager` dependency for netstandard2.0\n- refact `HttpRequester`, add `HttpClientHttpRequester`/`WebRequestHttpRequester`\n- add `NoProxyHttpClientHandler`\n\n### [WeihanLi.Common 1.0.19](https://www.nuget.org/packages/WeihanLi.Common/1.0.19)\n\n- Update `IDataSerializer` method `Desrializer` => `Deserialize`\n- update HttpRequester\n- update logging extensions\n\n### [WeihanLi.Common 1.0.17](https://www.nuget.org/packages/WeihanLi.Common/1.0.17)\n\n- Add `IDataCompressor`/`NullDataCompressor`\n- Update `RetryHelper`/`TotpHelper`\n\n### [WeihanLi.Common 1.0.15](https://www.nuget.org/packages/WeihanLi.Common/1.0.15)\n\n- update Repository\n- update `AsyncLock`\n\n### [WeihanLi.Common 1.0.14](https://www.nuget.org/packages/WeihanLi.Common/1.0.14)\n\n- update DataExtension, add support for DbConnection Extension select/fetch int\n- refact `HttpRequestClient` => `HttpRequester` with fluent api\n- add TotpHelper/ObjectId/ObjectIdGenerator\n"
  },
  {
    "path": "docs/api/.gitignore",
    "content": "###############\n#  temp file  #\n###############\n*.yml\n.manifest\n"
  },
  {
    "path": "docs/api/index.md",
    "content": "# PLACEHOLDER\n\nTODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*!\n"
  },
  {
    "path": "docs/articles/intro.md",
    "content": "# Add your introductions here!\n"
  },
  {
    "path": "docs/articles/toc.yml",
    "content": "- name: Introduction\n  href: intro.md\n"
  },
  {
    "path": "docs/docfx.json",
    "content": "{\n  \"metadata\": [\n    {\n      \"src\": [\n        {\n          \"files\": [\n            \"src/**.csproj\"\n          ],\n          \"src\": \"../\"\n        }\n      ],\n      \"dest\": \"api\",\n      \"includePrivateMembers\": false,\n      \"disableGitFeatures\": false,\n      \"disableDefaultFilter\": false,\n      \"noRestore\": false,\n      \"namespaceLayout\": \"flattened\"\n    }\n  ],\n  \"build\": {\n    \"content\": [\n      {\n        \"files\": [\n          \"api/**.yml\",\n          \"api/index.md\"\n        ]\n      },\n      {\n        \"files\": [\n          \"articles/**.md\",\n          \"articles/**/toc.yml\",\n          \"toc.yml\",\n          \"*.md\"\n        ]\n      }\n    ],\n    \"resource\": [\n      {\n        \"files\": [\n          \"images/**\"\n        ]\n      }\n    ],\n    \"overwrite\": [\n      {\n        \"files\": [\n          \"apidoc/**.md\"\n        ],\n        \"exclude\": [\n          \"obj/**\",\n          \"_site/**\"\n        ]\n      }\n    ],\n    \"dest\": \"_site\",\n    \"globalMetadataFiles\": [],\n    \"fileMetadataFiles\": [],\n    \"template\": [\n      \"default\",\n      \"modern\"\n    ],\n    \"postProcessors\": [],\n    \"noLangKeyword\": false,\n    \"keepFileLink\": false,\n    \"disableGitFeatures\": false\n  }\n}"
  },
  {
    "path": "docs/index.md",
    "content": "# WeihanLi.Common\n\n## Build status\n\n[![WeihanLi.Common Latest Stable](https://img.shields.io/nuget/v/WeihanLi.Common.svg)](https://www.nuget.org/packages/WeihanLi.Common/)\n\n[![WeihanLi.Common Latest](https://img.shields.io/nuget/vpre/WeihanLi.Common)](https://www.nuget.org/packages/WeihanLi.Common/absoluteLatest)\n\n## Intro\n\n.NET 常用帮助类，扩展方法等，基础类库\n\n## Packages\n\n与这个 Repository 相关的 nuget 包：\n\n- [WeihanLi.Common](https://www.nuget.org/packages/WeihanLi.Common) 基础组件\n- [WeihanLi.Common.Aspect.Castle](https://www.nuget.org/packages/WeihanLi.Common.Aspect.Castle/)  基于 Castle 的 AOP 扩展\n- [WeihanLi.Common.Aspect.AspectCore](https://www.nuget.org/packages/WeihanLi.Common.Aspect.Castle/)  基于 AspectCore 的 AOP 扩展（`CreateProxyWithTarget` 不支持 class)\n- [WeihanLi.Data](https://www.nuget.org/packages/WeihanLi.Data) 数据库扩展\n- [WeihanLi.Common.Logging.Serilog](https://www.nuget.org/packages/WeihanLi.Common.Logging.Serilog) 日志 serilog 扩展\n\n## Features\n\n- Dependence Injection(类比微软依赖注入框架自定义实现的依赖注入框架)\n- Fluent Aspects -- AOP implemented(基于动态代理实现的 AOP 框架)\n- Event Related(EventBus/EventQueue/EventStore)\n- Logging Framework(结合serilog/微软日志框架实现的日志框架)\n- Dapper-like Ado.Net extensions(类似 Dapper 的 Ado.Net 扩展)\n- TOTP implement(TOTP算法实现)\n- and more ...\n\n## Contact\n\nContact me if you need: <weihanli@outlook.com>\n"
  },
  {
    "path": "docs/toc.yml",
    "content": "- name: Articles\n  href: articles/\n- name: Api Documentation\n  href: api/\n- name: ReleaseNotes\n  href: ReleaseNotes.md\n- name: Github\n  href: https://github.com/WeihanLi/WeihanLi.Common"
  },
  {
    "path": "nuget.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <config>\n    <add key=\"defaultPushSource\" value=\"nuget\" />\n  </config>\n  <packageSources>\n    <!--To inherit the global NuGet package sources remove the <clear/> line below -->\n    <clear />\n    <add key=\"nuget\" value=\"https://api.nuget.org/v3/index.json\" />\n  </packageSources>\n</configuration>\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/CreateInstanceTest-report-github.md",
    "content": "``` ini\n\nBenchmarkDotNet=v0.10.14, OS=Windows 10.0.18362\nIntel Core i5-6300U CPU 2.40GHz (Skylake), 1 CPU, 4 logical and 2 physical cores\n.NET Core SDK=3.0.100\n  [Host]     : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT\n  DefaultJob : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT\n\n\n```\n|                       Method |      Mean |      Error |    StdDev |\n|----------------------------- |----------:|-----------:|----------:|\n|      NewInstanceByExpression |  17.02 ns |  0.7622 ns |  2.211 ns |\n|      NewInstanceByReflection |  75.10 ns |  2.4788 ns |  7.032 ns |\n| NewInstanceByActivatorHelper | 411.30 ns | 19.5452 ns | 57.014 ns |\n|                  NewInstance |  15.23 ns |  1.0839 ns |  3.196 ns |\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/CreateInstanceTest-report.csv",
    "content": "Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,RemoveOutliers,Affinity,Jit,Platform,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,EnvironmentVariables,Toolchain,IsBaseline,InvocationCount,IterationTime,LaunchCount,RunStrategy,TargetCount,UnrollFactor,WarmupCount,Mean,Error,StdDev\nNewInstanceByExpression,Default,False,Default,Default,Default,Default,Default,Default,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,16,Default,17.02 ns,0.7622 ns,2.211 ns\nNewInstanceByReflection,Default,False,Default,Default,Default,Default,Default,Default,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,16,Default,75.10 ns,2.4788 ns,7.032 ns\nNewInstanceByActivatorHelper,Default,False,Default,Default,Default,Default,Default,Default,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,16,Default,411.30 ns,19.5452 ns,57.014 ns\nNewInstance,Default,False,Default,Default,Default,Default,Default,Default,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,16,Default,15.23 ns,1.0839 ns,3.196 ns\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/CreateInstanceTest-report.html",
    "content": "<!DOCTYPE html>\n<html lang='en'>\n<head>\n<meta charset='utf-8' />\n<title>CreateInstanceTest</title>\n\n<style type=\"text/css\">\n\ttable { border-collapse: collapse; display: block; width: 100%; overflow: auto; }\n\ttd, th { padding: 6px 13px; border: 1px solid #ddd; }\n\ttr { background-color: #fff; border-top: 1px solid #ccc; }\n\ttr:nth-child(even) { background: #f8f8f8; }\n</style>\n</head>\n<body>\n<pre><code>\nBenchmarkDotNet=v0.10.14, OS=Windows 10.0.18362\nIntel Core i5-6300U CPU 2.40GHz (Skylake), 1 CPU, 4 logical and 2 physical cores\n.NET Core SDK=3.0.100\n  [Host]     : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT\n  DefaultJob : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT\n</code></pre>\n<pre><code></code></pre>\n\n<table>\n<thead><tr><th>                Method</th><th>Mean</th><th>Error</th><th>StdDev</th>\n</tr>\n</thead><tbody><tr><td>NewInstanceByExpression</td><td>17.02 ns</td><td>0.7622 ns</td><td>2.211 ns</td>\n</tr><tr><td>NewInstanceByReflection</td><td>75.10 ns</td><td>2.4788 ns</td><td>7.032 ns</td>\n</tr><tr><td>NewInstanceByActivatorHelper</td><td>411.30 ns</td><td>19.5452 ns</td><td>57.014 ns</td>\n</tr><tr><td>NewInstance</td><td>15.23 ns</td><td>1.0839 ns</td><td>3.196 ns</td>\n</tr></tbody></table>\n</body>\n</html>\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/DITest-report-github.md",
    "content": "``` ini\n\nBenchmarkDotNet=v0.10.14, OS=Windows 10.0.18362\nIntel Core i5-6300U CPU 2.40GHz (Skylake), 1 CPU, 4 logical and 2 physical cores\n.NET Core SDK=3.0.100\n  [Host]     : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT\n  Job-UURGLC : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT\n\nRemoveOutliers=False  Runtime=Core  LaunchCount=3  \nRunStrategy=Throughput  TargetCount=10  WarmupCount=5  \n\n```\n|                        Method |         Mean |     Error |     StdDev |       Median |          Op/s | Scaled | ScaledSD |      Gen 0 |  Allocated |\n|------------------------------ |-------------:|----------:|-----------:|-------------:|--------------:|-------:|---------:|-----------:|-----------:|\n|                          NoDI |     8.082 ns |  1.168 ns |   1.748 ns |     7.370 ns | 123,737,124.2 |   1.00 |     0.00 |   762.6953 |  1200000 B |\n|                     Singleton |   128.733 ns | 10.401 ns |  15.568 ns |   122.103 ns |   7,768,021.0 |  16.60 |     3.78 |          - |        0 B |\n|                        Scoped |   115.224 ns |  3.434 ns |   5.139 ns |   113.493 ns |   8,678,739.2 |  14.86 |     2.94 |          - |        0 B |\n|                     Transient |    61.175 ns |  7.712 ns |  11.542 ns |    55.817 ns |  16,346,632.9 |   7.89 |     2.13 |   761.7188 |  1200000 B |\n| ServiceContainerSingletonTest |   642.179 ns | 43.931 ns |  65.754 ns |   615.747 ns |   1,557,198.7 |  82.82 |    18.10 | 14437.5000 | 22800000 B |\n|    ServiceContainerScopedTest |   658.143 ns | 68.601 ns | 102.679 ns |   601.012 ns |   1,519,427.5 |  84.88 |    21.07 | 14468.7500 | 22800000 B |\n| ServiceContainerTransientTest | 1,790.600 ns | 55.919 ns |  83.698 ns | 1,775.545 ns |     558,472.0 | 230.92 |    45.85 | 46000.0000 | 72400000 B |\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/DITest-report.csv",
    "content": "Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,RemoveOutliers,Affinity,Jit,Platform,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,EnvironmentVariables,Toolchain,IsBaseline,InvocationCount,IterationTime,LaunchCount,RunStrategy,TargetCount,UnrollFactor,WarmupCount,Mean,Error,StdDev,Median,Op/s,Scaled,ScaledSD,Gen 0,Allocated\nNoDI,Default,False,Default,Default,Default,Default,Default,False,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,3,Throughput,10,16,5,8.082 ns,1.168 ns,1.748 ns,7.370 ns,\"123,737,124.2\",1.00,0.00,762.6953,1200000 B\nSingleton,Default,False,Default,Default,Default,Default,Default,False,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,3,Throughput,10,16,5,128.733 ns,10.401 ns,15.568 ns,122.103 ns,\"7,768,021.0\",16.60,3.78,-,0 B\nScoped,Default,False,Default,Default,Default,Default,Default,False,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,3,Throughput,10,16,5,115.224 ns,3.434 ns,5.139 ns,113.493 ns,\"8,678,739.2\",14.86,2.94,-,0 B\nTransient,Default,False,Default,Default,Default,Default,Default,False,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,3,Throughput,10,16,5,61.175 ns,7.712 ns,11.542 ns,55.817 ns,\"16,346,632.9\",7.89,2.13,761.7188,1200000 B\nServiceContainerSingletonTest,Default,False,Default,Default,Default,Default,Default,False,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,3,Throughput,10,16,5,642.179 ns,43.931 ns,65.754 ns,615.747 ns,\"1,557,198.7\",82.82,18.10,14437.5000,22800000 B\nServiceContainerScopedTest,Default,False,Default,Default,Default,Default,Default,False,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,3,Throughput,10,16,5,658.143 ns,68.601 ns,102.679 ns,601.012 ns,\"1,519,427.5\",84.88,21.07,14468.7500,22800000 B\nServiceContainerTransientTest,Default,False,Default,Default,Default,Default,Default,False,15,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,3,Throughput,10,16,5,\"1,790.600 ns\",55.919 ns,83.698 ns,\"1,775.545 ns\",\"558,472.0\",230.92,45.85,46000.0000,72400000 B\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/DITest-report.html",
    "content": "<!DOCTYPE html>\n<html lang='en'>\n<head>\n<meta charset='utf-8' />\n<title>DITest</title>\n\n<style type=\"text/css\">\n\ttable { border-collapse: collapse; display: block; width: 100%; overflow: auto; }\n\ttd, th { padding: 6px 13px; border: 1px solid #ddd; }\n\ttr { background-color: #fff; border-top: 1px solid #ccc; }\n\ttr:nth-child(even) { background: #f8f8f8; }\n</style>\n</head>\n<body>\n<pre><code>\nBenchmarkDotNet=v0.10.14, OS=Windows 10.0.18362\nIntel Core i5-6300U CPU 2.40GHz (Skylake), 1 CPU, 4 logical and 2 physical cores\n.NET Core SDK=3.0.100\n  [Host]     : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT\n  Job-UURGLC : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT\n</code></pre>\n<pre><code>RemoveOutliers=False  Runtime=Core  LaunchCount=3  \nRunStrategy=Throughput  TargetCount=10  WarmupCount=5  \n</code></pre>\n\n<table>\n<thead><tr><th>                 Method</th><th>  Mean</th><th>Error</th><th>StdDev</th><th>Median</th><th>   Op/s</th><th>Scaled</th><th>ScaledSD</th><th>Gen 0</th><th>Allocated</th>\n</tr>\n</thead><tbody><tr><td>NoDI</td><td>8.082 ns</td><td>1.168 ns</td><td>1.748 ns</td><td>7.370 ns</td><td>123,737,124.2</td><td>1.00</td><td>0.00</td><td>762.6953</td><td>1200000 B</td>\n</tr><tr><td>Singleton</td><td>128.733 ns</td><td>10.401 ns</td><td>15.568 ns</td><td>122.103 ns</td><td>7,768,021.0</td><td>16.60</td><td>3.78</td><td>-</td><td>0 B</td>\n</tr><tr><td>Scoped</td><td>115.224 ns</td><td>3.434 ns</td><td>5.139 ns</td><td>113.493 ns</td><td>8,678,739.2</td><td>14.86</td><td>2.94</td><td>-</td><td>0 B</td>\n</tr><tr><td>Transient</td><td>61.175 ns</td><td>7.712 ns</td><td>11.542 ns</td><td>55.817 ns</td><td>16,346,632.9</td><td>7.89</td><td>2.13</td><td>761.7188</td><td>1200000 B</td>\n</tr><tr><td>ServiceContainerSingletonTest</td><td>642.179 ns</td><td>43.931 ns</td><td>65.754 ns</td><td>615.747 ns</td><td>1,557,198.7</td><td>82.82</td><td>18.10</td><td>14437.5000</td><td>22800000 B</td>\n</tr><tr><td>ServiceContainerScopedTest</td><td>658.143 ns</td><td>68.601 ns</td><td>102.679 ns</td><td>601.012 ns</td><td>1,519,427.5</td><td>84.88</td><td>21.07</td><td>14468.7500</td><td>22800000 B</td>\n</tr><tr><td>ServiceContainerTransientTest</td><td>1,790.600 ns</td><td>55.919 ns</td><td>83.698 ns</td><td>1,775.545 ns</td><td>558,472.0</td><td>230.92</td><td>45.85</td><td>46000.0000</td><td>72400000 B</td>\n</tr></tbody></table>\n</body>\n</html>\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/WeihanLi.Common.Benchmark.DITest-report-github.md",
    "content": "``` ini\n\nBenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362\nIntel Core i5-3470 CPU 3.20GHz (Ivy Bridge), 1 CPU, 4 logical and 4 physical cores\n.NET Core SDK=3.0.100\n  [Host]     : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), X64 RyuJIT\n  DefaultJob : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), X64 RyuJIT\n\n\n```\n|                        Method |       Mean |     Error |    StdDev |          Op/s |  Ratio | RatioSD |  Gen 0 | Gen 1 | Gen 2 | Allocated |\n|------------------------------ |-----------:|----------:|----------:|--------------:|-------:|--------:|-------:|------:|------:|----------:|\n|                          NoDI |   6.294 ns | 0.0800 ns | 0.0748 ns | 158,885,471.4 |   1.00 |    0.00 | 0.0076 |     - |     - |      24 B |\n|                     Singleton | 100.930 ns | 0.3708 ns | 0.3468 ns |   9,907,897.6 |  16.04 |    0.17 |      - |     - |     - |         - |\n|                     Transient |  49.680 ns | 0.5275 ns | 0.4934 ns |  20,128,957.4 |   7.89 |    0.10 | 0.0076 |     - |     - |      24 B |\n| ServiceContainerSingletonTest | 348.255 ns | 6.6019 ns | 6.1754 ns |   2,871,459.3 |  55.34 |    1.33 | 0.1169 |     - |     - |     368 B |\n| ServiceContainerTransientTest | 841.700 ns | 1.9591 ns | 1.7367 ns |   1,188,072.3 | 133.68 |    1.66 | 0.2533 |     - |     - |     800 B |\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/WeihanLi.Common.Benchmark.DITest-report.csv",
    "content": "Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,Op/s,Ratio,RatioSD,Gen 0,Gen 1,Gen 2,Allocated\nNoDI,Default,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 2.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,6.294 ns,0.0800 ns,0.0748 ns,\"158,885,471.4\",1.00,0.00,0.0076,0.0000,0.0000,24 B\nSingleton,Default,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 2.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,100.930 ns,0.3708 ns,0.3468 ns,\"9,907,897.6\",16.04,0.17,0.0000,0.0000,0.0000,0 B\nTransient,Default,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 2.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,49.680 ns,0.5275 ns,0.4934 ns,\"20,128,957.4\",7.89,0.10,0.0076,0.0000,0.0000,24 B\nServiceContainerSingletonTest,Default,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 2.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,348.255 ns,6.6019 ns,6.1754 ns,\"2,871,459.3\",55.34,1.33,0.1169,0.0000,0.0000,368 B\nServiceContainerTransientTest,Default,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 2.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,841.700 ns,1.9591 ns,1.7367 ns,\"1,188,072.3\",133.68,1.66,0.2533,0.0000,0.0000,800 B\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/WeihanLi.Common.Benchmark.DITest-report.html",
    "content": "<!DOCTYPE html>\n<html lang='en'>\n<head>\n<meta charset='utf-8' />\n<title>WeihanLi.Common.Benchmark.DITest-20191125-101726</title>\n\n<style type=\"text/css\">\n\ttable { border-collapse: collapse; display: block; width: 100%; overflow: auto; }\n\ttd, th { padding: 6px 13px; border: 1px solid #ddd; text-align: right; }\n\ttr { background-color: #fff; border-top: 1px solid #ccc; }\n\ttr:nth-child(even) { background: #f8f8f8; }\n</style>\n</head>\n<body>\n<pre><code>\nBenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362\nIntel Core i5-3470 CPU 3.20GHz (Ivy Bridge), 1 CPU, 4 logical and 4 physical cores\n.NET Core SDK=3.0.100\n  [Host]     : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), X64 RyuJIT\n  DefaultJob : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), X64 RyuJIT\n</code></pre>\n<pre><code></code></pre>\n\n<table>\n<thead><tr><th>                 Method</th><th>Mean</th><th>Error</th><th>StdDev</th><th>   Op/s</th><th>Ratio</th><th>RatioSD</th><th>Gen 0</th><th>Gen 1</th><th>Gen 2</th><th>Allocated</th>\n</tr>\n</thead><tbody><tr><td>NoDI</td><td>6.294 ns</td><td>0.0800 ns</td><td>0.0748 ns</td><td>158,885,471.4</td><td>1.00</td><td>0.00</td><td>0.0076</td><td>-</td><td>-</td><td>24 B</td>\n</tr><tr><td>Singleton</td><td>100.930 ns</td><td>0.3708 ns</td><td>0.3468 ns</td><td>9,907,897.6</td><td>16.04</td><td>0.17</td><td>-</td><td>-</td><td>-</td><td>-</td>\n</tr><tr><td>Transient</td><td>49.680 ns</td><td>0.5275 ns</td><td>0.4934 ns</td><td>20,128,957.4</td><td>7.89</td><td>0.10</td><td>0.0076</td><td>-</td><td>-</td><td>24 B</td>\n</tr><tr><td>ServiceContainerSingletonTest</td><td>348.255 ns</td><td>6.6019 ns</td><td>6.1754 ns</td><td>2,871,459.3</td><td>55.34</td><td>1.33</td><td>0.1169</td><td>-</td><td>-</td><td>368 B</td>\n</tr><tr><td>ServiceContainerTransientTest</td><td>841.700 ns</td><td>1.9591 ns</td><td>1.7367 ns</td><td>1,188,072.3</td><td>133.68</td><td>1.66</td><td>0.2533</td><td>-</td><td>-</td><td>800 B</td>\n</tr></tbody></table>\n</body>\n</html>\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/WeihanLi.Common.Benchmark.MapperTest-report-github.md",
    "content": "``` ini\n\nBenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.610 (2004/?/20H1)\nIntel Core i5-6300U CPU 2.40GHz (Skylake), 1 CPU, 4 logical and 2 physical cores\n.NET Core SDK=5.0.100-rc.2.20479.15\n  [Host]     : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT\n  DefaultJob : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT\n\n\n```\n|              Method |       Mean |    Error |   StdDev |     Median | Ratio | RatioSD |\n|-------------------- |-----------:|---------:|---------:|-----------:|------:|--------:|\n| AutoMapperBenchmark |   167.6 ns |  4.51 ns | 12.57 ns |   161.4 ns |  1.00 |    0.00 |\n|  MapHelperBenchmark | 4,335.0 ns | 26.20 ns | 23.22 ns | 4,332.7 ns | 26.31 |    1.22 |\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/WeihanLi.Common.Benchmark.MapperTest-report.csv",
    "content": "Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,Median,Ratio,RatioSD\nAutoMapperBenchmark,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,167.6 ns,4.51 ns,12.57 ns,161.4 ns,1.00,0.00\nMapHelperBenchmark,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,\"4,335.0 ns\",26.20 ns,23.22 ns,\"4,332.7 ns\",26.31,1.22\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/WeihanLi.Common.Benchmark.MapperTest-report.html",
    "content": "<!DOCTYPE html>\n<html lang='en'>\n<head>\n<meta charset='utf-8' />\n<title>WeihanLi.Common.Benchmark.MapperTest-20201107-234017</title>\n\n<style type=\"text/css\">\n\ttable { border-collapse: collapse; display: block; width: 100%; overflow: auto; }\n\ttd, th { padding: 6px 13px; border: 1px solid #ddd; text-align: right; }\n\ttr { background-color: #fff; border-top: 1px solid #ccc; }\n\ttr:nth-child(even) { background: #f8f8f8; }\n</style>\n</head>\n<body>\n<pre><code>\nBenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.610 (2004/?/20H1)\nIntel Core i5-6300U CPU 2.40GHz (Skylake), 1 CPU, 4 logical and 2 physical cores\n.NET Core SDK=5.0.100-rc.2.20479.15\n  [Host]     : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT\n  DefaultJob : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT\n</code></pre>\n<pre><code></code></pre>\n\n<table>\n<thead><tr><th>       Method</th><th>Mean</th><th>Error</th><th>StdDev</th><th>Median</th><th>Ratio</th><th>RatioSD</th>\n</tr>\n</thead><tbody><tr><td>AutoMapperBenchmark</td><td>167.6 ns</td><td>4.51 ns</td><td>12.57 ns</td><td>161.4 ns</td><td>1.00</td><td>0.00</td>\n</tr><tr><td>MapHelperBenchmark</td><td>4,335.0 ns</td><td>26.20 ns</td><td>23.22 ns</td><td>4,332.7 ns</td><td>26.31</td><td>1.22</td>\n</tr></tbody></table>\n</body>\n</html>\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/WeihanLi.Common.Benchmark.PipelineTest-report-github.md",
    "content": "``` ini\n\nBenchmarkDotNet=v0.13.1, OS=Windows 10.0.22581\nIntel Core i5-6300U CPU 2.40GHz (Skylake), 1 CPU, 4 logical and 2 physical cores\n.NET SDK=7.0.100-preview.2.22153.17\n  [Host]     : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT\n  DefaultJob : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT\n\n\n```\n|                  Method |         Mean |      Error |     StdDev |       Median |      Gen 0 | Allocated |\n|------------------------ |-------------:|-----------:|-----------:|-------------:|-----------:|----------:|\n|       ValueTaskPipeline |     18.69 μs |   1.402 μs |   4.133 μs |     16.55 μs |    19.3176 |     30 KB |\n| ValueTaskPipelineInvoke | 21,669.82 μs | 403.984 μs | 449.027 μs | 21,635.27 μs | 19875.0000 | 30,470 KB |\n|            TaskPipeline |     14.00 μs |   0.279 μs |   0.443 μs |     13.87 μs |    19.3329 |     30 KB |\n|      TaskPipelineInvoke | 17,902.60 μs | 346.586 μs | 508.021 μs | 17,881.09 μs | 19875.0000 | 30,470 KB |\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/WeihanLi.Common.Benchmark.PipelineTest-report.csv",
    "content": "Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MemoryRandomization,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,Median,Gen 0,Allocated\nValueTaskPipeline,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 6.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,18.69 μs,1.402 μs,4.133 μs,16.55 μs,19.3176,30 KB\nValueTaskPipelineInvoke,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 6.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,\"21,669.82 μs\",403.984 μs,449.027 μs,\"21,635.27 μs\",19875.0000,\"30,470 KB\"\nTaskPipeline,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 6.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,14.00 μs,0.279 μs,0.443 μs,13.87 μs,19.3329,30 KB\nTaskPipelineInvoke,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 6.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,\"17,902.60 μs\",346.586 μs,508.021 μs,\"17,881.09 μs\",19875.0000,\"30,470 KB\"\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/BenchmarkDotNet.Artifacts/results/WeihanLi.Common.Benchmark.PipelineTest-report.html",
    "content": "<!DOCTYPE html>\n<html lang='en'>\n<head>\n<meta charset='utf-8' />\n<title>WeihanLi.Common.Benchmark.PipelineTest-20220404-002449</title>\n\n<style type=\"text/css\">\n\ttable { border-collapse: collapse; display: block; width: 100%; overflow: auto; }\n\ttd, th { padding: 6px 13px; border: 1px solid #ddd; text-align: right; }\n\ttr { background-color: #fff; border-top: 1px solid #ccc; }\n\ttr:nth-child(even) { background: #f8f8f8; }\n</style>\n</head>\n<body>\n<pre><code>\nBenchmarkDotNet=v0.13.1, OS=Windows 10.0.22581\nIntel Core i5-6300U CPU 2.40GHz (Skylake), 1 CPU, 4 logical and 2 physical cores\n.NET SDK=7.0.100-preview.2.22153.17\n  [Host]     : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT\n  DefaultJob : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT\n</code></pre>\n<pre><code></code></pre>\n\n<table>\n<thead><tr><th>           Method</th><th>  Mean</th><th>Error</th><th>StdDev</th><th>Median</th><th>Gen 0</th><th>Allocated</th>\n</tr>\n</thead><tbody><tr><td>ValueTaskPipeline</td><td>18.69 &mu;s</td><td>1.402 &mu;s</td><td>4.133 &mu;s</td><td>16.55 &mu;s</td><td>19.3176</td><td>30 KB</td>\n</tr><tr><td>ValueTaskPipelineInvoke</td><td>21,669.82 &mu;s</td><td>403.984 &mu;s</td><td>449.027 &mu;s</td><td>21,635.27 &mu;s</td><td>19875.0000</td><td>30,470 KB</td>\n</tr><tr><td>TaskPipeline</td><td>14.00 &mu;s</td><td>0.279 &mu;s</td><td>0.443 &mu;s</td><td>13.87 &mu;s</td><td>19.3329</td><td>30 KB</td>\n</tr><tr><td>TaskPipelineInvoke</td><td>17,902.60 &mu;s</td><td>346.586 &mu;s</td><td>508.021 &mu;s</td><td>17,881.09 &mu;s</td><td>19875.0000</td><td>30,470 KB</td>\n</tr></tbody></table>\n</body>\n</html>\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/DITest.cs",
    "content": "﻿using BenchmarkDotNet.Attributes;\nusing BenchmarkDotNet.Columns;\nusing BenchmarkDotNet.Configs;\nusing BenchmarkDotNet.Diagnosers;\nusing BenchmarkDotNet.Validators;\nusing Microsoft.Extensions.DependencyInjection;\nusing System.Runtime.CompilerServices;\nusing WeihanLi.Common.DependencyInjection;\n\nnamespace WeihanLi.Common.Benchmark;\n\n// https://github.com/aspnet/DependencyInjection/blob/rel/2.0.0/test/Microsoft.Extensions.DependencyInjection.Performance/GetServiceBenchmark.cs\n[Config(typeof(CoreConfig))]\npublic class DITest\n{\n    // refer to https://github.com/aspnet/DependencyInjection/blob/rel/2.0.0/test/Microsoft.Extensions.DependencyInjection.Performance/configs/CoreConfig.cs\n    private class CoreConfig : ManualConfig\n    {\n        public CoreConfig()\n        {\n            AddValidator(JitOptimizationsValidator.FailOnError);\n            AddDiagnoser(MemoryDiagnoser.Default);\n            AddColumn(StatisticColumn.OperationsPerSecond);\n        }\n    }\n\n    private const int OperationsPerInvoke = 50000;\n\n    private IServiceProvider _transientSp;\n    private IServiceScope _scopedSp;\n    private IServiceProvider _singletonSp;\n\n    private IServiceContainer _singletonContainer;\n    private IServiceContainer _scopedRootContainer;\n    private IServiceContainer _scopedContainer;\n    private IServiceContainer _transientContainer;\n\n    [GlobalSetup]\n    public void Setup()\n    {\n        var services = new ServiceCollection();\n        services.AddSingleton<A>();\n        services.AddSingleton<B>();\n        services.AddSingleton<C>();\n        _singletonSp = services.BuildServiceProvider();\n\n        services = new ServiceCollection();\n        services.AddScoped<A>();\n        services.AddScoped<B>();\n        services.AddScoped<C>();\n        _scopedSp = services.BuildServiceProvider().CreateScope();\n\n        services = new ServiceCollection();\n        services.AddTransient<A>();\n        services.AddTransient<B>();\n        services.AddTransient<C>();\n        _transientSp = services.BuildServiceProvider();\n\n        var containerBuilder = new ServiceContainerBuilder();\n        containerBuilder.AddSingleton<A>();\n        containerBuilder.AddSingleton<B>();\n        containerBuilder.AddSingleton<C>();\n\n        _singletonContainer = containerBuilder.Build();\n\n        containerBuilder = new ServiceContainerBuilder();\n        containerBuilder.AddScoped<A>();\n        containerBuilder.AddScoped<B>();\n        containerBuilder.AddScoped<C>();\n\n        _scopedRootContainer = containerBuilder.Build();\n        _scopedContainer = _scopedRootContainer.CreateScope();\n\n        containerBuilder = new ServiceContainerBuilder();\n        containerBuilder.AddTransient<A>();\n        containerBuilder.AddTransient<B>();\n        containerBuilder.AddTransient<C>();\n\n        _transientContainer = containerBuilder.Build();\n    }\n\n    [GlobalCleanup]\n    public void Cleanup()\n    {\n        _singletonContainer?.Dispose();\n        _scopedRootContainer?.Dispose();\n        _scopedContainer?.Dispose();\n        _transientContainer?.Dispose();\n    }\n\n    [Benchmark(Baseline = true, OperationsPerInvoke = OperationsPerInvoke)]\n    public void NoDI()\n    {\n        for (var i = 0; i < OperationsPerInvoke; i++)\n        {\n            var temp = new A(new B(new C()));\n            temp.Foo();\n        }\n    }\n\n    //[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]\n    //public void Reflection()\n    //{\n    //    for (var i = 0; i < OperationsPerInvoke; i++)\n    //    {\n    //        var temp = (A)Activator.CreateInstance(typeof(A),\n    //                Activator.CreateInstance(typeof(B),\n    //                    Activator.CreateInstance(typeof(C))))\n    //            ;\n    //        temp.Foo();\n    //    }\n    //}\n\n    [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]\n    public void Singleton()\n    {\n        for (var i = 0; i < OperationsPerInvoke; i++)\n        {\n            var temp = _singletonSp.GetService<A>();\n            temp.Foo();\n        }\n    }\n\n    //[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]\n    //public void Scoped()\n    //{\n    //    for (var i = 0; i < OperationsPerInvoke; i++)\n    //    {\n    //        var temp = _scopedSp.ServiceProvider.GetService<A>();\n    //        temp.Foo();\n    //    }\n    //}\n\n    [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]\n    public void Transient()\n    {\n        for (var i = 0; i < OperationsPerInvoke; i++)\n        {\n            var temp = _transientSp.GetService<A>();\n            temp.Foo();\n        }\n    }\n\n    [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]\n    public void ServiceContainerSingletonTest()\n    {\n        for (var i = 0; i < OperationsPerInvoke; i++)\n        {\n            var temp = _singletonContainer.GetService<A>();\n            temp.Foo();\n        }\n    }\n\n    //[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]\n    //public void ServiceContainerScopedTest()\n    //{\n    //    for (var i = 0; i < OperationsPerInvoke; i++)\n    //    {\n    //        var temp = _scopedContainer.GetService<A>();\n    //        temp.Foo();\n    //    }\n    //}\n\n    [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]\n    public void ServiceContainerTransientTest()\n    {\n        for (var i = 0; i < OperationsPerInvoke; i++)\n        {\n            var temp = _transientContainer.GetService<A>();\n            temp.Foo();\n        }\n    }\n\n    private class A\n    {\n        public A(B b)\n        {\n        }\n\n        [MethodImpl(MethodImplOptions.NoInlining)]\n        public void Foo()\n        {\n        }\n    }\n\n    private class B\n    {\n        public B(C c)\n        {\n        }\n    }\n\n    private class C\n    {\n    }\n}\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/PipelineTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing BenchmarkDotNet.Attributes;\nusing System.Diagnostics;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Benchmark;\n\n[MemoryDiagnoser]\npublic class PipelineTest\n{\n    private sealed class TestContext\n    {\n    }\n\n    [Benchmark]\n    public async Task ValueTaskPipeline()\n    {\n        var builder = PipelineBuilder.CreateValueAsync<TestContext>();\n        for (var i = 0; i < 100; i++)\n        {\n            builder.Use(async (context, next) =>\n            {\n                Debug.WriteLine(context.GetHashCode());\n                await next();\n            });\n        }\n        var pipeline = builder.Build();\n\n        var context = new TestContext();\n        await pipeline(context);\n    }\n\n    [Benchmark]\n    public async Task ValueTaskPipelineInvoke()\n    {\n        var builder = PipelineBuilder.CreateValueAsync<TestContext>();\n        builder.Use(async (context, next) =>\n        {\n            Debug.WriteLine(context.GetHashCode());\n            await next();\n        });\n        builder.Use(async (context, next) =>\n        {\n            Debug.WriteLine(context.GetHashCode());\n            await next();\n        });\n        builder.Use(async (context, next) =>\n        {\n            Debug.WriteLine(context.GetHashCode());\n            await next();\n        });\n        var pipeline = builder.Build();\n        for (var i = 0; i < 100000; i++)\n        {\n            var context = new TestContext();\n            await pipeline(context);\n        }\n    }\n\n    [Benchmark]\n    public async Task TaskPipeline()\n    {\n        var builder = PipelineBuilder.CreateAsync<TestContext>();\n        for (var i = 0; i < 100; i++)\n        {\n            builder.Use(async (context, next) =>\n            {\n                Debug.WriteLine(context.GetHashCode());\n                await next();\n            });\n        }\n        var pipeline = builder.Build();\n\n        var context = new TestContext();\n        await pipeline(context);\n    }\n\n\n    [Benchmark]\n    public async Task TaskPipelineInvoke()\n    {\n        var builder = PipelineBuilder.CreateAsync<TestContext>();\n        builder.Use(async (context, next) =>\n        {\n            Debug.WriteLine(context.GetHashCode());\n            await next();\n        });\n        builder.Use(async (context, next) =>\n        {\n            Debug.WriteLine(context.GetHashCode());\n            await next();\n        });\n        builder.Use(async (context, next) =>\n        {\n            Debug.WriteLine(context.GetHashCode());\n            await next();\n        });\n        var pipeline = builder.Build();\n        for (var i = 0; i < 100000; i++)\n        {\n            var context = new TestContext();\n            await pipeline(context);\n        }\n    }\n}\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/Program.cs",
    "content": "﻿using BenchmarkDotNet.Running;\n\nnamespace WeihanLi.Common.Benchmark;\n\npublic class Program\n{\n    public static void Main(string[] args)\n    {\n        // BenchmarkRunner.Run<MapperTest>();\n        // BenchmarkRunner.Run<CreateInstanceTest>();\n        // BenchmarkRunner.Run<DITest>();\n\n        BenchmarkRunner.Run<PipelineTest>();\n\n        Console.ReadLine();\n    }\n}\n"
  },
  {
    "path": "perf/WeihanLi.Common.Benchmark/WeihanLi.Common.Benchmark.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>$(LatestTargetFramework)</TargetFramework>\n    <OutputType>Exe</OutputType>\n    <Optimize>true</Optimize>\n    <Benchmark>True</Benchmark>\n    <Nullable>disable</Nullable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"BenchmarkDotNet\" />\n    <PackageReference Include=\"Dapper\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\WeihanLi.Common\\WeihanLi.Common.csproj\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "samples/AspNetCoreSample/AspNetCoreSample.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>$(LatestTargetFramework)</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\WeihanLi.Common\\WeihanLi.Common.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "samples/AspNetCoreSample/Controllers/EventsController.cs",
    "content": "﻿using AspNetCoreSample.Events;\nusing Microsoft.AspNetCore.Mvc;\n\nnamespace AspNetCoreSample.Controllers;\n\n[Route(\"api/[controller]\")]\npublic class EventsController : ControllerBase\n{\n    [HttpGet(\"pageViewCount\")]\n    public IActionResult Count()\n    {\n        return Ok(new { PageViewEventHandler.Count });\n    }\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/Controllers/HomeController.cs",
    "content": "﻿using AspNetCoreSample.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing System.Diagnostics;\n\nnamespace AspNetCoreSample.Controllers;\n\npublic class HomeController : Controller\n{\n    public IActionResult Index()\n    {\n        return View();\n    }\n\n    public IActionResult About()\n    {\n        ViewData[\"Message\"] = \"Your application description page.\";\n\n        return View();\n    }\n\n    public IActionResult Contact()\n    {\n        ViewData[\"Message\"] = \"Your contact page.\";\n\n        return View();\n    }\n\n    public IActionResult Error()\n    {\n        return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });\n    }\n\n    /// <summary>\n    /// Upload file\n    /// upload file in asp.net core mvc\n    /// https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads\n    /// </summary>\n    /// <param name=\"formFile\">fileInfo</param>\n    /// <returns></returns>\n    [HttpPost]\n    public string Upload(IFormFile? formFile)\n    {\n        if (formFile == null)\n        {\n            return \"failed\";\n        }\n        Console.WriteLine(formFile.Name);\n        return \"success\";\n    }\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/Events/EventConsumer.cs",
    "content": "﻿using WeihanLi.Common;\nusing WeihanLi.Common.Event;\nusing WeihanLi.Extensions;\n\nnamespace AspNetCoreSample.Events;\n\npublic class EventConsumer\n  (IEventQueue eventQueue, IEventHandlerFactory eventHandlerFactory)\n  : BackgroundService\n{\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        while (!stoppingToken.IsCancellationRequested)\n        {\n            var queues = await eventQueue.GetQueuesAsync();\n            if (queues.Count > 0)\n            {\n                await queues.Select(async q =>\n                        {\n                            await foreach (var e in eventQueue.ReadAllAsync(q, stoppingToken))\n                            {\n                                var @event = e.Data;\n                                Guard.NotNull(@event);\n                                var handlers = eventHandlerFactory.GetHandlers(@event.GetType());\n                                if (handlers.Count > 0)\n                                {\n                                    await handlers\n                                            .Select(h => h.Handle(@event, e.Properties))\n                                            .WhenAll()\n                                        ;\n                                }\n                            }\n                        })\n                        .WhenAll()\n                    ;\n            }\n\n            await Task.Delay(1000, stoppingToken);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/Events/PageViewEvent.cs",
    "content": "﻿using WeihanLi.Common.Event;\n\nnamespace AspNetCoreSample.Events;\n\npublic class PageViewEvent\n{\n    public string? Path { get; set; }\n}\n\npublic class PageViewEventHandler : EventHandlerBase<PageViewEvent>\n{\n    public static int Count;\n\n    public override Task Handle(PageViewEvent @event, EventProperties eventProperties)\n    {\n        Interlocked.Increment(ref Count);\n        return Task.CompletedTask;\n    }\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/FluentAspectsServiceProviderFactory.cs",
    "content": "﻿using System.Linq.Expressions;\nusing WeihanLi.Common.Aspect;\nusing WeihanLi.Extensions;\n\nnamespace AspNetCoreSample;\n\ninternal sealed class FluentAspectsServiceProviderFactory(\n    Action<FluentAspectOptions>? optionsAction,\n    Action<IFluentAspectsBuilder>? aspectBuildAction,\n    Expression<Func<Type, bool>>? ignoreTypesPredict\n        ) : IServiceProviderFactory<IServiceCollection>\n{\n    private readonly Action<FluentAspectOptions>? _optionsAction = optionsAction;\n    private readonly Action<IFluentAspectsBuilder>? _aspectBuildAction = aspectBuildAction;\n    private readonly Expression<Func<Type, bool>>? _ignoreTypesPredict = ignoreTypesPredict;\n\n    public IServiceCollection CreateBuilder(IServiceCollection services)\n    {\n        return services;\n    }\n\n    public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)\n    {\n        return containerBuilder.BuildFluentAspectsProvider(_optionsAction, _aspectBuildAction, _ignoreTypesPredict);\n    }\n}\n\npublic static class HostBuilderExtensions\n{\n    public static IHostBuilder UseFluentAspectsServiceProviderFactory(this IHostBuilder hostBuilder,\n        Action<FluentAspectOptions>? optionsAction,\n        Action<IFluentAspectsBuilder>? aspectBuildAction = null,\n        Expression<Func<Type, bool>>? ignoreTypesPredict = null)\n    {\n        ignoreTypesPredict ??= t =>\n                t.HasNamespace() &&\n                (t.Namespace!.StartsWith(\"Microsoft.\")\n                || t.Namespace.StartsWith(\"System.\")\n                )\n                ;\n        hostBuilder.UseServiceProviderFactory(\n            new FluentAspectsServiceProviderFactory(optionsAction, aspectBuildAction, ignoreTypesPredict)\n            );\n        return hostBuilder;\n    }\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/LogInterceptor.cs",
    "content": "﻿using System.Diagnostics;\nusing WeihanLi.Common.Aspect;\nusing WeihanLi.Extensions;\n\nnamespace AspNetCoreSample;\n\npublic class EventPublishLogInterceptor : AbstractInterceptor\n{\n    public override async Task Invoke(IInvocation invocation, Func<Task> next)\n    {\n        Console.WriteLine(\"-------------------------------\");\n        Console.WriteLine($\"Event publish begin, eventData:{invocation.Arguments.ToJson()}\");\n        var watch = Stopwatch.StartNew();\n        try\n        {\n            await next();\n        }\n        catch (Exception ex)\n        {\n            Console.WriteLine($\"Event publish exception({ex})\");\n        }\n        finally\n        {\n            watch.Stop();\n            Console.WriteLine($\"Event publish complete, elasped:{watch.ElapsedMilliseconds} ms\");\n        }\n        Console.WriteLine(\"-------------------------------\");\n    }\n}\n\npublic class EventHandleLogInterceptor : IInterceptor\n{\n    public async Task Invoke(IInvocation invocation, Func<Task> next)\n    {\n        Console.WriteLine(\"-------------------------------\");\n        Console.WriteLine($\"Event handle begin, eventData:{invocation.Arguments.ToJson()}\");\n        var watch = Stopwatch.StartNew();\n        try\n        {\n            await next();\n        }\n        catch (Exception ex)\n        {\n            Console.WriteLine($\"Event handle exception({ex})\");\n        }\n        finally\n        {\n            watch.Stop();\n            Console.WriteLine($\"Event handle complete, elasped:{watch.ElapsedMilliseconds} ms\");\n        }\n        Console.WriteLine(\"-------------------------------\");\n    }\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/Models/ErrorViewModel.cs",
    "content": "﻿namespace AspNetCoreSample.Models;\n\npublic class ErrorViewModel\n{\n    public string? RequestId { get; set; }\n\n    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/Program.cs",
    "content": "﻿using WeihanLi.Common.Aspect;\nusing WeihanLi.Common.Event;\n\nnamespace AspNetCoreSample;\n\npublic class Program\n{\n    public static void Main(string[] args)\n    {\n        var host = Host.CreateDefaultBuilder(args)\n            .ConfigureWebHostDefaults(builder =>\n            {\n                builder.UseStartup<Startup>();\n            })\n            //.UseServiceProviderFactory()\n            .UseFluentAspectsServiceProviderFactory(options =>\n            {\n                options\n                    .InterceptType<IEventPublisher>()\n                    .With<EventPublishLogInterceptor>();\n\n                options.InterceptType<IEventHandler>()\n                    .With<EventHandleLogInterceptor>();\n            }, builder =>\n            {\n                //builder.UseCastleProxy();\n            })\n            .Build();\n        //var fields = host.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic)\n        //    ;\n        host.Run();\n    }\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/Startup.cs",
    "content": "﻿using AspNetCoreSample.Events;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Event;\n\nnamespace AspNetCoreSample;\n\npublic class Startup(IConfiguration configuration)\n{\n    public IConfiguration Configuration { get; } = configuration.ReplacePlaceholders();\n\n    // This method gets called by the runtime. Use this method to add services to the container.\n    public void ConfigureServices(IServiceCollection services)\n    {\n        services.AddControllersWithViews()\n            ;\n\n        services.AddEvents()\n            .AddEventHandler<PageViewEvent, PageViewEventHandler>()\n            ;\n\n        services.AddSingleton<IEventPublisher, EventQueuePublisher>();\n        services.AddHostedService<EventConsumer>();\n\n        // TestReplaceHolder\n        var abc = Configuration[\"TestSetting2:Setting2\"];\n        Console.WriteLine(abc);\n    }\n\n    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.\n    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)\n    {\n        // Init service locator\n        DependencyResolver.SetDependencyResolver(app.ApplicationServices);\n\n        if (env.IsDevelopment())\n        {\n            app.UseDeveloperExceptionPage();\n        }\n        else\n        {\n            app.UseExceptionHandler(\"/Home/Error\");\n        }\n\n        // pageView middleware\n        app.Use(async (context, next) =>\n        {\n            var eventPublisher = context.RequestServices\n                .GetRequiredService<IEventPublisher>();\n            await eventPublisher.PublishAsync(new PageViewEvent()\n            {\n                Path = context.Request.Path.Value ?? \"\",\n            });\n\n            await next();\n        });\n        app.UseHttpLogging();\n        app.UseRouting();\n\n        app.UseEndpoints(endpoint =>\n        {\n            endpoint.MapControllers();\n            endpoint.MapDefaultControllerRoute();\n        });\n    }\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/Views/Home/About.cshtml",
    "content": "﻿@{\n    ViewData[\"Title\"] = \"About\";\n}\n<h2>@ViewData[\"Title\"]</h2>\n<h3>@ViewData[\"Message\"]</h3>\n\n<p>Use this area to provide additional information.</p>\n"
  },
  {
    "path": "samples/AspNetCoreSample/Views/Home/Contact.cshtml",
    "content": "﻿@{\n    ViewData[\"Title\"] = \"Contact\";\n}\n<h2>@ViewData[\"Title\"]</h2>\n<h3>@ViewData[\"Message\"]</h3>\n\n<address>\n    One Microsoft Way<br />\n    Redmond, WA 98052-6399<br />\n    <abbr title=\"Phone\">P:</abbr>\n    425.555.0100\n</address>\n\n<address>\n    <strong>Support:</strong> <a href=\"mailto:Support@example.com\">Support@example.com</a><br />\n    <strong>Marketing:</strong> <a href=\"mailto:Marketing@example.com\">Marketing@example.com</a>\n</address>\n"
  },
  {
    "path": "samples/AspNetCoreSample/Views/Home/Index.cshtml",
    "content": "﻿@{\n    ViewData[\"Title\"] = \"Home Page\";\n}\n\n<div id=\"myCarousel\" class=\"carousel slide\" data-ride=\"carousel\" data-interval=\"6000\">\n    <ol class=\"carousel-indicators\">\n        <li data-target=\"#myCarousel\" data-slide-to=\"0\" class=\"active\"></li>\n        <li data-target=\"#myCarousel\" data-slide-to=\"1\"></li>\n        <li data-target=\"#myCarousel\" data-slide-to=\"2\"></li>\n        <li data-target=\"#myCarousel\" data-slide-to=\"3\"></li>\n    </ol>\n    <div class=\"carousel-inner\" role=\"listbox\">\n        <div class=\"item active\">\n            <img src=\"~/images/banner1.svg\" alt=\"ASP.NET\" class=\"img-responsive\" />\n            <div class=\"carousel-caption\" role=\"option\">\n                <p>\n                    Learn how to build ASP.NET apps that can run anywhere.\n                    <a class=\"btn btn-default\" href=\"https://go.microsoft.com/fwlink/?LinkID=525028&clcid=0x409\">\n                        Learn More\n                    </a>\n                </p>\n            </div>\n        </div>\n        <div class=\"item\">\n            <img src=\"~/images/banner2.svg\" alt=\"Visual Studio\" class=\"img-responsive\" />\n            <div class=\"carousel-caption\" role=\"option\">\n                <p>\n                    There are powerful new features in Visual Studio for building modern web apps.\n                    <a class=\"btn btn-default\" href=\"https://go.microsoft.com/fwlink/?LinkID=525030&clcid=0x409\">\n                        Learn More\n                    </a>\n                </p>\n            </div>\n        </div>\n        <div class=\"item\">\n            <img src=\"~/images/banner3.svg\" alt=\"Package Management\" class=\"img-responsive\" />\n            <div class=\"carousel-caption\" role=\"option\">\n                <p>\n                    Bring in libraries from NuGet and npm, and automate tasks using Grunt or Gulp.\n                    <a class=\"btn btn-default\" href=\"https://go.microsoft.com/fwlink/?LinkID=525029&clcid=0x409\">\n                        Learn More\n                    </a>\n                </p>\n            </div>\n        </div>\n        <div class=\"item\">\n            <img src=\"~/images/banner4.svg\" alt=\"Microsoft Azure\" class=\"img-responsive\" />\n            <div class=\"carousel-caption\" role=\"option\">\n                <p>\n                    Learn how Microsoft's Azure cloud platform allows you to build, deploy, and scale web apps.\n                    <a class=\"btn btn-default\" href=\"https://go.microsoft.com/fwlink/?LinkID=525027&clcid=0x409\">\n                        Learn More\n                    </a>\n                </p>\n            </div>\n        </div>\n    </div>\n    <a class=\"left carousel-control\" href=\"#myCarousel\" role=\"button\" data-slide=\"prev\">\n        <span class=\"glyphicon glyphicon-chevron-left\" aria-hidden=\"true\"></span>\n        <span class=\"sr-only\">Previous</span>\n    </a>\n    <a class=\"right carousel-control\" href=\"#myCarousel\" role=\"button\" data-slide=\"next\">\n        <span class=\"glyphicon glyphicon-chevron-right\" aria-hidden=\"true\"></span>\n        <span class=\"sr-only\">Next</span>\n    </a>\n</div>\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        <h2>Application uses</h2>\n        <ul>\n            <li>Sample pages using ASP.NET Core MVC</li>\n            <li>Theming using <a href=\"https://go.microsoft.com/fwlink/?LinkID=398939\">Bootstrap</a></li>\n        </ul>\n    </div>\n    <div class=\"col-md-3\">\n        <h2>How to</h2>\n        <ul>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkID=398600\">Add a Controller and View</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkId=699315\">Manage User Secrets using Secret Manager.</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkId=699316\">Use logging to log a message.</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkId=699317\">Add packages using NuGet.</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkId=699319\">Target development, staging or production environment.</a></li>\n        </ul>\n    </div>\n    <div class=\"col-md-3\">\n        <h2>Overview</h2>\n        <ul>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkId=518008\">Conceptual overview of what is ASP.NET Core</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkId=699320\">Fundamentals of ASP.NET Core such as Startup and middleware.</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkId=398602\">Working with Data</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkId=398603\">Security</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkID=699321\">Client side development</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkID=699322\">Develop on different platforms</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkID=699323\">Read more on the documentation site</a></li>\n        </ul>\n    </div>\n    <div class=\"col-md-3\">\n        <h2>Run &amp; Deploy</h2>\n        <ul>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkID=517851\">Run your app</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkID=517853\">Run tools such as EF migrations and more</a></li>\n            <li><a href=\"https://go.microsoft.com/fwlink/?LinkID=398609\">Publish to Microsoft Azure Web Apps</a></li>\n        </ul>\n    </div>\n</div>\n"
  },
  {
    "path": "samples/AspNetCoreSample/Views/Shared/Error.cshtml",
    "content": "﻿@model ErrorViewModel\n@{\n    ViewData[\"Title\"] = \"Error\";\n}\n\n<h1 class=\"text-danger\">Error.</h1>\n<h2 class=\"text-danger\">An error occurred while processing your request.</h2>\n\n@if (Model?.ShowRequestId == true)\n{\n    <p>\n        <strong>Request ID:</strong> <code>@Model.RequestId</code>\n    </p>\n}\n\n<h3>Development Mode</h3>\n<p>\n    Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.\n</p>\n<p>\n    <strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.\n</p>\n"
  },
  {
    "path": "samples/AspNetCoreSample/Views/Shared/_Layout.cshtml",
    "content": "﻿<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>@ViewData[\"Title\"] - AspNetCoreSample</title>\n\n    <environment include=\"Development\">\n        <link rel=\"stylesheet\" href=\"~/lib/bootstrap/dist/css/bootstrap.css\" />\n        <link rel=\"stylesheet\" href=\"~/css/site.css\" />\n    </environment>\n    <environment exclude=\"Development\">\n        <link rel=\"stylesheet\" href=\"https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css\"\n              asp-fallback-href=\"~/lib/bootstrap/dist/css/bootstrap.min.css\"\n              asp-fallback-test-class=\"sr-only\" asp-fallback-test-property=\"position\" asp-fallback-test-value=\"absolute\" />\n        <link rel=\"stylesheet\" href=\"~/css/site.min.css\" asp-append-version=\"true\" />\n    </environment>\n</head>\n<body>\n    <nav class=\"navbar navbar-inverse navbar-fixed-top\">\n        <div class=\"container\">\n            <div class=\"navbar-header\">\n                <button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\".navbar-collapse\">\n                    <span class=\"sr-only\">Toggle navigation</span>\n                    <span class=\"icon-bar\"></span>\n                    <span class=\"icon-bar\"></span>\n                    <span class=\"icon-bar\"></span>\n                </button>\n                <a asp-area=\"\" asp-controller=\"Home\" asp-action=\"Index\" class=\"navbar-brand\">AspNetCoreSample</a>\n            </div>\n            <div class=\"navbar-collapse collapse\">\n                <ul class=\"nav navbar-nav\">\n                    <li><a asp-area=\"\" asp-controller=\"Home\" asp-action=\"Index\">Home</a></li>\n                    <li><a asp-area=\"\" asp-controller=\"Home\" asp-action=\"About\">About</a></li>\n                    <li><a asp-area=\"\" asp-controller=\"Home\" asp-action=\"Contact\">Contact</a></li>\n                </ul>\n            </div>\n        </div>\n    </nav>\n    <div class=\"container body-content\">\n        @RenderBody()\n        <hr />\n        <footer>\n            <p>&copy; 2018 - AspNetCoreSample</p>\n        </footer>\n    </div>\n\n    <environment include=\"Development\">\n        <script src=\"~/lib/jquery/dist/jquery.js\"></script>\n        <script src=\"~/lib/bootstrap/dist/js/bootstrap.js\"></script>\n        <script src=\"~/js/site.js\" asp-append-version=\"true\"></script>\n    </environment>\n    <environment exclude=\"Development\">\n        <script src=\"https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js\"\n                asp-fallback-src=\"~/lib/jquery/dist/jquery.min.js\"\n                asp-fallback-test=\"window.jQuery\"\n                crossorigin=\"anonymous\"\n                integrity=\"sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk\">\n        </script>\n        <script src=\"https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js\"\n                asp-fallback-src=\"~/lib/bootstrap/dist/js/bootstrap.min.js\"\n                asp-fallback-test=\"window.jQuery && window.jQuery.fn && window.jQuery.fn.modal\"\n                crossorigin=\"anonymous\"\n                integrity=\"sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa\">\n        </script>\n        <script src=\"~/js/site.min.js\" asp-append-version=\"true\"></script>\n    </environment>\n\n    @RenderSection(\"Scripts\", required: false)\n</body>\n</html>\n"
  },
  {
    "path": "samples/AspNetCoreSample/Views/Shared/_ValidationScriptsPartial.cshtml",
    "content": "<environment include=\"Development\">\n    <script src=\"~/lib/jquery-validation/dist/jquery.validate.js\"></script>\n    <script src=\"~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js\"></script>\n</environment>\n<environment exclude=\"Development\">\n    <script src=\"https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js\"\n            asp-fallback-src=\"~/lib/jquery-validation/dist/jquery.validate.min.js\"\n            asp-fallback-test=\"window.jQuery && window.jQuery.validator\"\n            crossorigin=\"anonymous\"\n            integrity=\"sha384-Fnqn3nxp3506LP/7Y3j/25BlWeA3PXTyT1l78LjECcPaKCV12TsZP7yyMxOe/G/k\">\n    </script>\n    <script src=\"https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js\"\n            asp-fallback-src=\"~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js\"\n            asp-fallback-test=\"window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive\"\n            crossorigin=\"anonymous\"\n            integrity=\"sha384-JrXK+k53HACyavUKOsL+NkmSesD2P+73eDMrbTtTk0h4RmOF8hF8apPlkp26JlyH\">\n    </script>\n</environment>\n"
  },
  {
    "path": "samples/AspNetCoreSample/Views/_ViewImports.cshtml",
    "content": "@using AspNetCoreSample\n@using AspNetCoreSample.Models\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n"
  },
  {
    "path": "samples/AspNetCoreSample/Views/_ViewStart.cshtml",
    "content": "﻿@{\n    Layout = \"_Layout\";\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/appsettings.json",
    "content": "﻿{\n  \"TestSetting\": {\n    \"Setting1\": \"AAA\"\n  },\n  \"TestSetting2\": {\n    \"Setting2\": \"$(TestSetting:Setting1)\"\n  },\n  \"Logging\": {\n    \"IncludeScopes\": false,\n    \"LogLevel\": {\n      \"Default\": \"Warning\"\n    }\n  }\n}"
  },
  {
    "path": "samples/AspNetCoreSample/bundleconfig.json",
    "content": "﻿// Configure bundling and minification for the project.\n// More info at https://go.microsoft.com/fwlink/?LinkId=808241\n[\n  {\n    \"outputFileName\": \"wwwroot/css/site.min.css\",\n    // An array of relative input file paths. Globbing patterns supported\n    \"inputFiles\": [\n      \"wwwroot/css/site.css\"\n    ]\n  },\n  {\n    \"outputFileName\": \"wwwroot/js/site.min.js\",\n    \"inputFiles\": [\n      \"wwwroot/js/site.js\"\n    ],\n    // Optionally specify minification options\n    \"minify\": {\n      \"enabled\": true,\n      \"renameLocals\": true\n    },\n    // Optionally generate .map file\n    \"sourceMap\": false\n  }\n]\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/css/site.css",
    "content": "﻿body {\n    padding-top: 50px;\n    padding-bottom: 20px;\n}\n\n/* Wrapping element */\n/* Set some basic padding to keep content from hitting the edges */\n.body-content {\n    padding-left: 15px;\n    padding-right: 15px;\n}\n\n/* Carousel */\n.carousel-caption p {\n    font-size: 20px;\n    line-height: 1.4;\n}\n\n/* Make .svg files in the carousel display properly in older browsers */\n.carousel-inner .item img[src$=\".svg\"] {\n    width: 100%;\n}\n\n/* QR code generator */\n#qrCode {\n    margin: 15px;\n}\n\n/* Hide/rearrange for smaller screens */\n@media screen and (max-width: 767px) {\n    /* Hide captions */\n    .carousel-caption {\n        display: none;\n    }\n}\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/js/site.js",
    "content": "﻿// Write your JavaScript code.\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/bootstrap/.bower.json",
    "content": "{\n  \"name\": \"bootstrap\",\n  \"description\": \"The most popular front-end framework for developing responsive, mobile first projects on the web.\",\n  \"keywords\": [\n    \"css\",\n    \"js\",\n    \"less\",\n    \"mobile-first\",\n    \"responsive\",\n    \"front-end\",\n    \"framework\",\n    \"web\"\n  ],\n  \"homepage\": \"http://getbootstrap.com\",\n  \"license\": \"MIT\",\n  \"moduleType\": \"globals\",\n  \"main\": [\n    \"less/bootstrap.less\",\n    \"dist/js/bootstrap.js\"\n  ],\n  \"ignore\": [\n    \"/.*\",\n    \"_config.yml\",\n    \"CNAME\",\n    \"composer.json\",\n    \"CONTRIBUTING.md\",\n    \"docs\",\n    \"js/tests\",\n    \"test-infra\"\n  ],\n  \"dependencies\": {\n    \"jquery\": \"1.9.1 - 3\"\n  },\n  \"version\": \"3.3.7\",\n  \"_release\": \"3.3.7\",\n  \"_resolution\": {\n    \"type\": \"version\",\n    \"tag\": \"v3.3.7\",\n    \"commit\": \"0b9c4a4007c44201dce9a6cc1a38407005c26c86\"\n  },\n  \"_source\": \"https://github.com/twbs/bootstrap.git\",\n  \"_target\": \"v3.3.7\",\n  \"_originalSource\": \"bootstrap\",\n  \"_direct\": true\n}"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/bootstrap/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2011-2016 Twitter, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css",
    "content": "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n  text-shadow: none;\n}\n.btn:active,\n.btn.active {\n  background-image: none;\n}\n.btn-default {\n  text-shadow: 0 1px 0 #fff;\n  background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n  background-image:      -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));\n  background-image:         linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #dbdbdb;\n  border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n  background-color: #e0e0e0;\n  background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n  background-color: #e0e0e0;\n  border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n  background-color: #e0e0e0;\n  background-image: none;\n}\n.btn-primary {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n  background-image:      -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));\n  background-image:         linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n  background-color: #265a88;\n  background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n  background-color: #265a88;\n  border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n  background-color: #265a88;\n  background-image: none;\n}\n.btn-success {\n  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n  background-image:      -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));\n  background-image:         linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n  background-color: #419641;\n  background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n  background-color: #419641;\n  border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n  background-color: #419641;\n  background-image: none;\n}\n.btn-info {\n  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n  background-image:      -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));\n  background-image:         linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n  background-color: #2aabd2;\n  background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n  background-color: #2aabd2;\n  border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n  background-color: #2aabd2;\n  background-image: none;\n}\n.btn-warning {\n  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));\n  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n  background-color: #eb9316;\n  background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n  background-color: #eb9316;\n  border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n  background-color: #eb9316;\n  background-image: none;\n}\n.btn-danger {\n  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n  background-image:      -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));\n  background-image:         linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n  background-color: #c12e2a;\n  background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n  background-color: #c12e2a;\n  border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n  background-color: #c12e2a;\n  background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  background-color: #e8e8e8;\n  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));\n  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n  background-repeat: repeat-x;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  background-color: #2e6da4;\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));\n  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n  background-repeat: repeat-x;\n}\n.navbar-default {\n  background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);\n  background-image:      -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));\n  background-image:         linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n  background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n  background-image:      -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));\n  background-image:         linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n  background-repeat: repeat-x;\n  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);\n          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, .25);\n}\n.navbar-inverse {\n  background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n  background-image:      -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));\n  background-image:         linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n  background-repeat: repeat-x;\n  border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n  background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n  background-image:      -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));\n  background-image:         linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n  background-repeat: repeat-x;\n  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);\n          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  border-radius: 0;\n}\n@media (max-width: 767px) {\n  .navbar .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #fff;\n    background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n    background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n    background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));\n    background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n    background-repeat: repeat-x;\n  }\n}\n.alert {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, .2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);\n}\n.alert-success {\n  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n  background-image:      -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));\n  background-image:         linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n  background-repeat: repeat-x;\n  border-color: #b2dba1;\n}\n.alert-info {\n  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n  background-image:      -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));\n  background-image:         linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n  background-repeat: repeat-x;\n  border-color: #9acfea;\n}\n.alert-warning {\n  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));\n  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n  background-repeat: repeat-x;\n  border-color: #f5e79e;\n}\n.alert-danger {\n  background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n  background-image:      -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));\n  background-image:         linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n  background-repeat: repeat-x;\n  border-color: #dca7a7;\n}\n.progress {\n  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n  background-image:      -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));\n  background-image:         linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n  background-repeat: repeat-x;\n}\n.progress-bar {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n  background-image:      -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));\n  background-image:         linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n  background-repeat: repeat-x;\n}\n.progress-bar-success {\n  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n  background-image:      -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));\n  background-image:         linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n  background-repeat: repeat-x;\n}\n.progress-bar-info {\n  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n  background-image:      -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));\n  background-image:         linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n  background-repeat: repeat-x;\n}\n.progress-bar-warning {\n  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));\n  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n  background-repeat: repeat-x;\n}\n.progress-bar-danger {\n  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n  background-image:      -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));\n  background-image:         linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n  background-repeat: repeat-x;\n}\n.progress-bar-striped {\n  background-image: -webkit-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);\n  background-image:      -o-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);\n  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);\n}\n.list-group {\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  text-shadow: 0 -1px 0 #286090;\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n  background-image:      -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));\n  background-image:         linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n  background-repeat: repeat-x;\n  border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n  text-shadow: none;\n}\n.panel {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, .05);\n}\n.panel-default > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));\n  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n  background-repeat: repeat-x;\n}\n.panel-primary > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));\n  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n  background-repeat: repeat-x;\n}\n.panel-success > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n  background-image:      -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));\n  background-image:         linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n  background-repeat: repeat-x;\n}\n.panel-info > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n  background-image:      -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));\n  background-image:         linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n  background-repeat: repeat-x;\n}\n.panel-warning > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));\n  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n  background-repeat: repeat-x;\n}\n.panel-danger > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n  background-image:      -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));\n  background-image:         linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n  background-repeat: repeat-x;\n}\n.well {\n  background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n  background-image:      -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n  background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));\n  background-image:         linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n  background-repeat: repeat-x;\n  border-color: #dcdcdc;\n  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);\n          box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/bootstrap/dist/css/bootstrap.css",
    "content": "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n  font-family: sans-serif;\n  -webkit-text-size-adjust: 100%;\n      -ms-text-size-adjust: 100%;\n}\nbody {\n  margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block;\n  vertical-align: baseline;\n}\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n[hidden],\ntemplate {\n  display: none;\n}\na {\n  background-color: transparent;\n}\na:active,\na:hover {\n  outline: 0;\n}\nabbr[title] {\n  border-bottom: 1px dotted;\n}\nb,\nstrong {\n  font-weight: bold;\n}\ndfn {\n  font-style: italic;\n}\nh1 {\n  margin: .67em 0;\n  font-size: 2em;\n}\nmark {\n  color: #000;\n  background: #ff0;\n}\nsmall {\n  font-size: 80%;\n}\nsub,\nsup {\n  position: relative;\n  font-size: 75%;\n  line-height: 0;\n  vertical-align: baseline;\n}\nsup {\n  top: -.5em;\n}\nsub {\n  bottom: -.25em;\n}\nimg {\n  border: 0;\n}\nsvg:not(:root) {\n  overflow: hidden;\n}\nfigure {\n  margin: 1em 40px;\n}\nhr {\n  height: 0;\n  -webkit-box-sizing: content-box;\n     -moz-box-sizing: content-box;\n          box-sizing: content-box;\n}\npre {\n  overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  margin: 0;\n  font: inherit;\n  color: inherit;\n}\nbutton {\n  overflow: visible;\n}\nbutton,\nselect {\n  text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button;\n  cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  padding: 0;\n  border: 0;\n}\ninput {\n  line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n  padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\ninput[type=\"search\"] {\n  -webkit-box-sizing: content-box;\n     -moz-box-sizing: content-box;\n          box-sizing: content-box;\n  -webkit-appearance: textfield;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\nfieldset {\n  padding: .35em .625em .75em;\n  margin: 0 2px;\n  border: 1px solid #c0c0c0;\n}\nlegend {\n  padding: 0;\n  border: 0;\n}\ntextarea {\n  overflow: auto;\n}\noptgroup {\n  font-weight: bold;\n}\ntable {\n  border-spacing: 0;\n  border-collapse: collapse;\n}\ntd,\nth {\n  padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n  *,\n  *:before,\n  *:after {\n    color: #000 !important;\n    text-shadow: none !important;\n    background: transparent !important;\n    -webkit-box-shadow: none !important;\n            box-shadow: none !important;\n  }\n  a,\n  a:visited {\n    text-decoration: underline;\n  }\n  a[href]:after {\n    content: \" (\" attr(href) \")\";\n  }\n  abbr[title]:after {\n    content: \" (\" attr(title) \")\";\n  }\n  a[href^=\"#\"]:after,\n  a[href^=\"javascript:\"]:after {\n    content: \"\";\n  }\n  pre,\n  blockquote {\n    border: 1px solid #999;\n\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  img {\n    max-width: 100% !important;\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  .navbar {\n    display: none;\n  }\n  .btn > .caret,\n  .dropup > .btn > .caret {\n    border-top-color: #000 !important;\n  }\n  .label {\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@font-face {\n  font-family: 'Glyphicons Halflings';\n\n  src: url('../fonts/glyphicons-halflings-regular.eot');\n  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n  content: \"\\002a\";\n}\n.glyphicon-plus:before {\n  content: \"\\002b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n  content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n  content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n  content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n  content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n  content: \"\\270f\";\n}\n.glyphicon-glass:before {\n  content: \"\\e001\";\n}\n.glyphicon-music:before {\n  content: \"\\e002\";\n}\n.glyphicon-search:before {\n  content: \"\\e003\";\n}\n.glyphicon-heart:before {\n  content: \"\\e005\";\n}\n.glyphicon-star:before {\n  content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n  content: \"\\e007\";\n}\n.glyphicon-user:before {\n  content: \"\\e008\";\n}\n.glyphicon-film:before {\n  content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n  content: \"\\e010\";\n}\n.glyphicon-th:before {\n  content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n  content: \"\\e012\";\n}\n.glyphicon-ok:before {\n  content: \"\\e013\";\n}\n.glyphicon-remove:before {\n  content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n  content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n  content: \"\\e016\";\n}\n.glyphicon-off:before {\n  content: \"\\e017\";\n}\n.glyphicon-signal:before {\n  content: \"\\e018\";\n}\n.glyphicon-cog:before {\n  content: \"\\e019\";\n}\n.glyphicon-trash:before {\n  content: \"\\e020\";\n}\n.glyphicon-home:before {\n  content: \"\\e021\";\n}\n.glyphicon-file:before {\n  content: \"\\e022\";\n}\n.glyphicon-time:before {\n  content: \"\\e023\";\n}\n.glyphicon-road:before {\n  content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n  content: \"\\e025\";\n}\n.glyphicon-download:before {\n  content: \"\\e026\";\n}\n.glyphicon-upload:before {\n  content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n  content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n  content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n  content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n  content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n  content: \"\\e032\";\n}\n.glyphicon-lock:before {\n  content: \"\\e033\";\n}\n.glyphicon-flag:before {\n  content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n  content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n  content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n  content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n  content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n  content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n  content: \"\\e040\";\n}\n.glyphicon-tag:before {\n  content: \"\\e041\";\n}\n.glyphicon-tags:before {\n  content: \"\\e042\";\n}\n.glyphicon-book:before {\n  content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n  content: \"\\e044\";\n}\n.glyphicon-print:before {\n  content: \"\\e045\";\n}\n.glyphicon-camera:before {\n  content: \"\\e046\";\n}\n.glyphicon-font:before {\n  content: \"\\e047\";\n}\n.glyphicon-bold:before {\n  content: \"\\e048\";\n}\n.glyphicon-italic:before {\n  content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n  content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n  content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n  content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n  content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n  content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n  content: \"\\e055\";\n}\n.glyphicon-list:before {\n  content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n  content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n  content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n  content: \"\\e059\";\n}\n.glyphicon-picture:before {\n  content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n  content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n  content: \"\\e063\";\n}\n.glyphicon-tint:before {\n  content: \"\\e064\";\n}\n.glyphicon-edit:before {\n  content: \"\\e065\";\n}\n.glyphicon-share:before {\n  content: \"\\e066\";\n}\n.glyphicon-check:before {\n  content: \"\\e067\";\n}\n.glyphicon-move:before {\n  content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n  content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n  content: \"\\e070\";\n}\n.glyphicon-backward:before {\n  content: \"\\e071\";\n}\n.glyphicon-play:before {\n  content: \"\\e072\";\n}\n.glyphicon-pause:before {\n  content: \"\\e073\";\n}\n.glyphicon-stop:before {\n  content: \"\\e074\";\n}\n.glyphicon-forward:before {\n  content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n  content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n  content: \"\\e077\";\n}\n.glyphicon-eject:before {\n  content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n  content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n  content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n  content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n  content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n  content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n  content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n  content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n  content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n  content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n  content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n  content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n  content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n  content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n  content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n  content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n  content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n  content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n  content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n  content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n  content: \"\\e101\";\n}\n.glyphicon-gift:before {\n  content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n  content: \"\\e103\";\n}\n.glyphicon-fire:before {\n  content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n  content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n  content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n  content: \"\\e107\";\n}\n.glyphicon-plane:before {\n  content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n  content: \"\\e109\";\n}\n.glyphicon-random:before {\n  content: \"\\e110\";\n}\n.glyphicon-comment:before {\n  content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n  content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n  content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n  content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n  content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n  content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n  content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n  content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n  content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n  content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n  content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n  content: \"\\e122\";\n}\n.glyphicon-bell:before {\n  content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n  content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n  content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n  content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n  content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n  content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n  content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n  content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n  content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n  content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n  content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n  content: \"\\e134\";\n}\n.glyphicon-globe:before {\n  content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n  content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n  content: \"\\e137\";\n}\n.glyphicon-filter:before {\n  content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n  content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n  content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n  content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n  content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n  content: \"\\e143\";\n}\n.glyphicon-link:before {\n  content: \"\\e144\";\n}\n.glyphicon-phone:before {\n  content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n  content: \"\\e146\";\n}\n.glyphicon-usd:before {\n  content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n  content: \"\\e149\";\n}\n.glyphicon-sort:before {\n  content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n  content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n  content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n  content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n  content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n  content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n  content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n  content: \"\\e157\";\n}\n.glyphicon-expand:before {\n  content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n  content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n  content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n  content: \"\\e161\";\n}\n.glyphicon-flash:before {\n  content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n  content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n  content: \"\\e164\";\n}\n.glyphicon-record:before {\n  content: \"\\e165\";\n}\n.glyphicon-save:before {\n  content: \"\\e166\";\n}\n.glyphicon-open:before {\n  content: \"\\e167\";\n}\n.glyphicon-saved:before {\n  content: \"\\e168\";\n}\n.glyphicon-import:before {\n  content: \"\\e169\";\n}\n.glyphicon-export:before {\n  content: \"\\e170\";\n}\n.glyphicon-send:before {\n  content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n  content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n  content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n  content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n  content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n  content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n  content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n  content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n  content: \"\\e179\";\n}\n.glyphicon-header:before {\n  content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n  content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n  content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n  content: \"\\e183\";\n}\n.glyphicon-tower:before {\n  content: \"\\e184\";\n}\n.glyphicon-stats:before {\n  content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n  content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n  content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n  content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n  content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n  content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n  content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n  content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n  content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n  content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n  content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n  content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n  content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n  content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n  content: \"\\e200\";\n}\n.glyphicon-cd:before {\n  content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n  content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n  content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n  content: \"\\e204\";\n}\n.glyphicon-copy:before {\n  content: \"\\e205\";\n}\n.glyphicon-paste:before {\n  content: \"\\e206\";\n}\n.glyphicon-alert:before {\n  content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n  content: \"\\e210\";\n}\n.glyphicon-king:before {\n  content: \"\\e211\";\n}\n.glyphicon-queen:before {\n  content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n  content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n  content: \"\\e214\";\n}\n.glyphicon-knight:before {\n  content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n  content: \"\\e216\";\n}\n.glyphicon-tent:before {\n  content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n  content: \"\\e218\";\n}\n.glyphicon-bed:before {\n  content: \"\\e219\";\n}\n.glyphicon-apple:before {\n  content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n  content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n  content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n  content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n  content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n  content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n  content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n  content: \"\\e227\";\n}\n.glyphicon-btc:before {\n  content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n  content: \"\\e227\";\n}\n.glyphicon-yen:before {\n  content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n  content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n  content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n  content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n  content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n  content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n  content: \"\\e232\";\n}\n.glyphicon-education:before {\n  content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n  content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n  content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n  content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n  content: \"\\e237\";\n}\n.glyphicon-oil:before {\n  content: \"\\e238\";\n}\n.glyphicon-grain:before {\n  content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n  content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n  content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n  content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n  content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n  content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n  content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n  content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n  content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n  content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n  content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n  content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n  content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n  content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n  content: \"\\e253\";\n}\n.glyphicon-console:before {\n  content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n  content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n  content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n  content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n  content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n  content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n  content: \"\\e260\";\n}\n* {\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\n*:before,\n*:after {\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\nhtml {\n  font-size: 10px;\n\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #333;\n  background-color: #fff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\na {\n  color: #337ab7;\n  text-decoration: none;\n}\na:hover,\na:focus {\n  color: #23527c;\n  text-decoration: underline;\n}\na:focus {\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\nfigure {\n  margin: 0;\n}\nimg {\n  vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  display: block;\n  max-width: 100%;\n  height: auto;\n}\n.img-rounded {\n  border-radius: 6px;\n}\n.img-thumbnail {\n  display: inline-block;\n  max-width: 100%;\n  height: auto;\n  padding: 4px;\n  line-height: 1.42857143;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  border-radius: 4px;\n  -webkit-transition: all .2s ease-in-out;\n       -o-transition: all .2s ease-in-out;\n          transition: all .2s ease-in-out;\n}\n.img-circle {\n  border-radius: 50%;\n}\nhr {\n  margin-top: 20px;\n  margin-bottom: 20px;\n  border: 0;\n  border-top: 1px solid #eee;\n}\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  margin: 0;\n  overflow: visible;\n  clip: auto;\n}\n[role=\"button\"] {\n  cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n  font-family: inherit;\n  font-weight: 500;\n  line-height: 1.1;\n  color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n  font-weight: normal;\n  line-height: 1;\n  color: #777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n  margin-top: 20px;\n  margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n  font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n  font-size: 75%;\n}\nh1,\n.h1 {\n  font-size: 36px;\n}\nh2,\n.h2 {\n  font-size: 30px;\n}\nh3,\n.h3 {\n  font-size: 24px;\n}\nh4,\n.h4 {\n  font-size: 18px;\n}\nh5,\n.h5 {\n  font-size: 14px;\n}\nh6,\n.h6 {\n  font-size: 12px;\n}\np {\n  margin: 0 0 10px;\n}\n.lead {\n  margin-bottom: 20px;\n  font-size: 16px;\n  font-weight: 300;\n  line-height: 1.4;\n}\n@media (min-width: 768px) {\n  .lead {\n    font-size: 21px;\n  }\n}\nsmall,\n.small {\n  font-size: 85%;\n}\nmark,\n.mark {\n  padding: .2em;\n  background-color: #fcf8e3;\n}\n.text-left {\n  text-align: left;\n}\n.text-right {\n  text-align: right;\n}\n.text-center {\n  text-align: center;\n}\n.text-justify {\n  text-align: justify;\n}\n.text-nowrap {\n  white-space: nowrap;\n}\n.text-lowercase {\n  text-transform: lowercase;\n}\n.text-uppercase {\n  text-transform: uppercase;\n}\n.text-capitalize {\n  text-transform: capitalize;\n}\n.text-muted {\n  color: #777;\n}\n.text-primary {\n  color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n  color: #286090;\n}\n.text-success {\n  color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n  color: #2b542c;\n}\n.text-info {\n  color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n  color: #245269;\n}\n.text-warning {\n  color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n  color: #66512c;\n}\n.text-danger {\n  color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n  color: #843534;\n}\n.bg-primary {\n  color: #fff;\n  background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n  background-color: #286090;\n}\n.bg-success {\n  background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n  background-color: #c1e2b3;\n}\n.bg-info {\n  background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n  background-color: #afd9ee;\n}\n.bg-warning {\n  background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n  background-color: #f7ecb5;\n}\n.bg-danger {\n  background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n  background-color: #e4b9b9;\n}\n.page-header {\n  padding-bottom: 9px;\n  margin: 40px 0 20px;\n  border-bottom: 1px solid #eee;\n}\nul,\nol {\n  margin-top: 0;\n  margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n  margin-bottom: 0;\n}\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n.list-inline {\n  padding-left: 0;\n  margin-left: -5px;\n  list-style: none;\n}\n.list-inline > li {\n  display: inline-block;\n  padding-right: 5px;\n  padding-left: 5px;\n}\ndl {\n  margin-top: 0;\n  margin-bottom: 20px;\n}\ndt,\ndd {\n  line-height: 1.42857143;\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin-left: 0;\n}\n@media (min-width: 768px) {\n  .dl-horizontal dt {\n    float: left;\n    width: 160px;\n    overflow: hidden;\n    clear: left;\n    text-align: right;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n  .dl-horizontal dd {\n    margin-left: 180px;\n  }\n}\nabbr[title],\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted #777;\n}\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\nblockquote {\n  padding: 10px 20px;\n  margin: 0 0 20px;\n  font-size: 17.5px;\n  border-left: 5px solid #eee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n  margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n  display: block;\n  font-size: 80%;\n  line-height: 1.42857143;\n  color: #777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n  content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  text-align: right;\n  border-right: 5px solid #eee;\n  border-left: 0;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n  content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n  content: '\\00A0 \\2014';\n}\naddress {\n  margin-bottom: 20px;\n  font-style: normal;\n  line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n  font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #c7254e;\n  background-color: #f9f2f4;\n  border-radius: 4px;\n}\nkbd {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #fff;\n  background-color: #333;\n  border-radius: 3px;\n  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);\n          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);\n}\nkbd kbd {\n  padding: 0;\n  font-size: 100%;\n  font-weight: bold;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\npre {\n  display: block;\n  padding: 9.5px;\n  margin: 0 0 10px;\n  font-size: 13px;\n  line-height: 1.42857143;\n  color: #333;\n  word-break: break-all;\n  word-wrap: break-word;\n  background-color: #f5f5f5;\n  border: 1px solid #ccc;\n  border-radius: 4px;\n}\npre code {\n  padding: 0;\n  font-size: inherit;\n  color: inherit;\n  white-space: pre-wrap;\n  background-color: transparent;\n  border-radius: 0;\n}\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n.container {\n  padding-right: 15px;\n  padding-left: 15px;\n  margin-right: auto;\n  margin-left: auto;\n}\n@media (min-width: 768px) {\n  .container {\n    width: 750px;\n  }\n}\n@media (min-width: 992px) {\n  .container {\n    width: 970px;\n  }\n}\n@media (min-width: 1200px) {\n  .container {\n    width: 1170px;\n  }\n}\n.container-fluid {\n  padding-right: 15px;\n  padding-left: 15px;\n  margin-right: auto;\n  margin-left: auto;\n}\n.row {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n  position: relative;\n  min-height: 1px;\n  padding-right: 15px;\n  padding-left: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n  float: left;\n}\n.col-xs-12 {\n  width: 100%;\n}\n.col-xs-11 {\n  width: 91.66666667%;\n}\n.col-xs-10 {\n  width: 83.33333333%;\n}\n.col-xs-9 {\n  width: 75%;\n}\n.col-xs-8 {\n  width: 66.66666667%;\n}\n.col-xs-7 {\n  width: 58.33333333%;\n}\n.col-xs-6 {\n  width: 50%;\n}\n.col-xs-5 {\n  width: 41.66666667%;\n}\n.col-xs-4 {\n  width: 33.33333333%;\n}\n.col-xs-3 {\n  width: 25%;\n}\n.col-xs-2 {\n  width: 16.66666667%;\n}\n.col-xs-1 {\n  width: 8.33333333%;\n}\n.col-xs-pull-12 {\n  right: 100%;\n}\n.col-xs-pull-11 {\n  right: 91.66666667%;\n}\n.col-xs-pull-10 {\n  right: 83.33333333%;\n}\n.col-xs-pull-9 {\n  right: 75%;\n}\n.col-xs-pull-8 {\n  right: 66.66666667%;\n}\n.col-xs-pull-7 {\n  right: 58.33333333%;\n}\n.col-xs-pull-6 {\n  right: 50%;\n}\n.col-xs-pull-5 {\n  right: 41.66666667%;\n}\n.col-xs-pull-4 {\n  right: 33.33333333%;\n}\n.col-xs-pull-3 {\n  right: 25%;\n}\n.col-xs-pull-2 {\n  right: 16.66666667%;\n}\n.col-xs-pull-1 {\n  right: 8.33333333%;\n}\n.col-xs-pull-0 {\n  right: auto;\n}\n.col-xs-push-12 {\n  left: 100%;\n}\n.col-xs-push-11 {\n  left: 91.66666667%;\n}\n.col-xs-push-10 {\n  left: 83.33333333%;\n}\n.col-xs-push-9 {\n  left: 75%;\n}\n.col-xs-push-8 {\n  left: 66.66666667%;\n}\n.col-xs-push-7 {\n  left: 58.33333333%;\n}\n.col-xs-push-6 {\n  left: 50%;\n}\n.col-xs-push-5 {\n  left: 41.66666667%;\n}\n.col-xs-push-4 {\n  left: 33.33333333%;\n}\n.col-xs-push-3 {\n  left: 25%;\n}\n.col-xs-push-2 {\n  left: 16.66666667%;\n}\n.col-xs-push-1 {\n  left: 8.33333333%;\n}\n.col-xs-push-0 {\n  left: auto;\n}\n.col-xs-offset-12 {\n  margin-left: 100%;\n}\n.col-xs-offset-11 {\n  margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n  margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n  margin-left: 75%;\n}\n.col-xs-offset-8 {\n  margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n  margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n  margin-left: 50%;\n}\n.col-xs-offset-5 {\n  margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n  margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n  margin-left: 25%;\n}\n.col-xs-offset-2 {\n  margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n  margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n  margin-left: 0;\n}\n@media (min-width: 768px) {\n  .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 {\n    float: left;\n  }\n  .col-sm-12 {\n    width: 100%;\n  }\n  .col-sm-11 {\n    width: 91.66666667%;\n  }\n  .col-sm-10 {\n    width: 83.33333333%;\n  }\n  .col-sm-9 {\n    width: 75%;\n  }\n  .col-sm-8 {\n    width: 66.66666667%;\n  }\n  .col-sm-7 {\n    width: 58.33333333%;\n  }\n  .col-sm-6 {\n    width: 50%;\n  }\n  .col-sm-5 {\n    width: 41.66666667%;\n  }\n  .col-sm-4 {\n    width: 33.33333333%;\n  }\n  .col-sm-3 {\n    width: 25%;\n  }\n  .col-sm-2 {\n    width: 16.66666667%;\n  }\n  .col-sm-1 {\n    width: 8.33333333%;\n  }\n  .col-sm-pull-12 {\n    right: 100%;\n  }\n  .col-sm-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-sm-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-sm-pull-9 {\n    right: 75%;\n  }\n  .col-sm-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-sm-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-sm-pull-6 {\n    right: 50%;\n  }\n  .col-sm-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-sm-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-sm-pull-3 {\n    right: 25%;\n  }\n  .col-sm-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-sm-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-sm-pull-0 {\n    right: auto;\n  }\n  .col-sm-push-12 {\n    left: 100%;\n  }\n  .col-sm-push-11 {\n    left: 91.66666667%;\n  }\n  .col-sm-push-10 {\n    left: 83.33333333%;\n  }\n  .col-sm-push-9 {\n    left: 75%;\n  }\n  .col-sm-push-8 {\n    left: 66.66666667%;\n  }\n  .col-sm-push-7 {\n    left: 58.33333333%;\n  }\n  .col-sm-push-6 {\n    left: 50%;\n  }\n  .col-sm-push-5 {\n    left: 41.66666667%;\n  }\n  .col-sm-push-4 {\n    left: 33.33333333%;\n  }\n  .col-sm-push-3 {\n    left: 25%;\n  }\n  .col-sm-push-2 {\n    left: 16.66666667%;\n  }\n  .col-sm-push-1 {\n    left: 8.33333333%;\n  }\n  .col-sm-push-0 {\n    left: auto;\n  }\n  .col-sm-offset-12 {\n    margin-left: 100%;\n  }\n  .col-sm-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-sm-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-sm-offset-9 {\n    margin-left: 75%;\n  }\n  .col-sm-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-sm-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-sm-offset-6 {\n    margin-left: 50%;\n  }\n  .col-sm-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-sm-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-sm-offset-3 {\n    margin-left: 25%;\n  }\n  .col-sm-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-sm-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-sm-offset-0 {\n    margin-left: 0;\n  }\n}\n@media (min-width: 992px) {\n  .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 {\n    float: left;\n  }\n  .col-md-12 {\n    width: 100%;\n  }\n  .col-md-11 {\n    width: 91.66666667%;\n  }\n  .col-md-10 {\n    width: 83.33333333%;\n  }\n  .col-md-9 {\n    width: 75%;\n  }\n  .col-md-8 {\n    width: 66.66666667%;\n  }\n  .col-md-7 {\n    width: 58.33333333%;\n  }\n  .col-md-6 {\n    width: 50%;\n  }\n  .col-md-5 {\n    width: 41.66666667%;\n  }\n  .col-md-4 {\n    width: 33.33333333%;\n  }\n  .col-md-3 {\n    width: 25%;\n  }\n  .col-md-2 {\n    width: 16.66666667%;\n  }\n  .col-md-1 {\n    width: 8.33333333%;\n  }\n  .col-md-pull-12 {\n    right: 100%;\n  }\n  .col-md-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-md-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-md-pull-9 {\n    right: 75%;\n  }\n  .col-md-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-md-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-md-pull-6 {\n    right: 50%;\n  }\n  .col-md-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-md-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-md-pull-3 {\n    right: 25%;\n  }\n  .col-md-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-md-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-md-pull-0 {\n    right: auto;\n  }\n  .col-md-push-12 {\n    left: 100%;\n  }\n  .col-md-push-11 {\n    left: 91.66666667%;\n  }\n  .col-md-push-10 {\n    left: 83.33333333%;\n  }\n  .col-md-push-9 {\n    left: 75%;\n  }\n  .col-md-push-8 {\n    left: 66.66666667%;\n  }\n  .col-md-push-7 {\n    left: 58.33333333%;\n  }\n  .col-md-push-6 {\n    left: 50%;\n  }\n  .col-md-push-5 {\n    left: 41.66666667%;\n  }\n  .col-md-push-4 {\n    left: 33.33333333%;\n  }\n  .col-md-push-3 {\n    left: 25%;\n  }\n  .col-md-push-2 {\n    left: 16.66666667%;\n  }\n  .col-md-push-1 {\n    left: 8.33333333%;\n  }\n  .col-md-push-0 {\n    left: auto;\n  }\n  .col-md-offset-12 {\n    margin-left: 100%;\n  }\n  .col-md-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-md-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-md-offset-9 {\n    margin-left: 75%;\n  }\n  .col-md-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-md-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-md-offset-6 {\n    margin-left: 50%;\n  }\n  .col-md-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-md-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-md-offset-3 {\n    margin-left: 25%;\n  }\n  .col-md-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-md-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-md-offset-0 {\n    margin-left: 0;\n  }\n}\n@media (min-width: 1200px) {\n  .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 {\n    float: left;\n  }\n  .col-lg-12 {\n    width: 100%;\n  }\n  .col-lg-11 {\n    width: 91.66666667%;\n  }\n  .col-lg-10 {\n    width: 83.33333333%;\n  }\n  .col-lg-9 {\n    width: 75%;\n  }\n  .col-lg-8 {\n    width: 66.66666667%;\n  }\n  .col-lg-7 {\n    width: 58.33333333%;\n  }\n  .col-lg-6 {\n    width: 50%;\n  }\n  .col-lg-5 {\n    width: 41.66666667%;\n  }\n  .col-lg-4 {\n    width: 33.33333333%;\n  }\n  .col-lg-3 {\n    width: 25%;\n  }\n  .col-lg-2 {\n    width: 16.66666667%;\n  }\n  .col-lg-1 {\n    width: 8.33333333%;\n  }\n  .col-lg-pull-12 {\n    right: 100%;\n  }\n  .col-lg-pull-11 {\n    right: 91.66666667%;\n  }\n  .col-lg-pull-10 {\n    right: 83.33333333%;\n  }\n  .col-lg-pull-9 {\n    right: 75%;\n  }\n  .col-lg-pull-8 {\n    right: 66.66666667%;\n  }\n  .col-lg-pull-7 {\n    right: 58.33333333%;\n  }\n  .col-lg-pull-6 {\n    right: 50%;\n  }\n  .col-lg-pull-5 {\n    right: 41.66666667%;\n  }\n  .col-lg-pull-4 {\n    right: 33.33333333%;\n  }\n  .col-lg-pull-3 {\n    right: 25%;\n  }\n  .col-lg-pull-2 {\n    right: 16.66666667%;\n  }\n  .col-lg-pull-1 {\n    right: 8.33333333%;\n  }\n  .col-lg-pull-0 {\n    right: auto;\n  }\n  .col-lg-push-12 {\n    left: 100%;\n  }\n  .col-lg-push-11 {\n    left: 91.66666667%;\n  }\n  .col-lg-push-10 {\n    left: 83.33333333%;\n  }\n  .col-lg-push-9 {\n    left: 75%;\n  }\n  .col-lg-push-8 {\n    left: 66.66666667%;\n  }\n  .col-lg-push-7 {\n    left: 58.33333333%;\n  }\n  .col-lg-push-6 {\n    left: 50%;\n  }\n  .col-lg-push-5 {\n    left: 41.66666667%;\n  }\n  .col-lg-push-4 {\n    left: 33.33333333%;\n  }\n  .col-lg-push-3 {\n    left: 25%;\n  }\n  .col-lg-push-2 {\n    left: 16.66666667%;\n  }\n  .col-lg-push-1 {\n    left: 8.33333333%;\n  }\n  .col-lg-push-0 {\n    left: auto;\n  }\n  .col-lg-offset-12 {\n    margin-left: 100%;\n  }\n  .col-lg-offset-11 {\n    margin-left: 91.66666667%;\n  }\n  .col-lg-offset-10 {\n    margin-left: 83.33333333%;\n  }\n  .col-lg-offset-9 {\n    margin-left: 75%;\n  }\n  .col-lg-offset-8 {\n    margin-left: 66.66666667%;\n  }\n  .col-lg-offset-7 {\n    margin-left: 58.33333333%;\n  }\n  .col-lg-offset-6 {\n    margin-left: 50%;\n  }\n  .col-lg-offset-5 {\n    margin-left: 41.66666667%;\n  }\n  .col-lg-offset-4 {\n    margin-left: 33.33333333%;\n  }\n  .col-lg-offset-3 {\n    margin-left: 25%;\n  }\n  .col-lg-offset-2 {\n    margin-left: 16.66666667%;\n  }\n  .col-lg-offset-1 {\n    margin-left: 8.33333333%;\n  }\n  .col-lg-offset-0 {\n    margin-left: 0;\n  }\n}\ntable {\n  background-color: transparent;\n}\ncaption {\n  padding-top: 8px;\n  padding-bottom: 8px;\n  color: #777;\n  text-align: left;\n}\nth {\n  text-align: left;\n}\n.table {\n  width: 100%;\n  max-width: 100%;\n  margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n  padding: 8px;\n  line-height: 1.42857143;\n  vertical-align: top;\n  border-top: 1px solid #ddd;\n}\n.table > thead > tr > th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #ddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n  border-top: 0;\n}\n.table > tbody + tbody {\n  border-top: 2px solid #ddd;\n}\n.table .table {\n  background-color: #fff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n  padding: 5px;\n}\n.table-bordered {\n  border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n  border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n  border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n  background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n  background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n  position: static;\n  display: table-column;\n  float: none;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n  position: static;\n  display: table-cell;\n  float: none;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n  background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n  background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n  background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n  background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n  background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n  background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n  background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n  background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n  background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n  background-color: #ebcccc;\n}\n.table-responsive {\n  min-height: .01%;\n  overflow-x: auto;\n}\n@media screen and (max-width: 767px) {\n  .table-responsive {\n    width: 100%;\n    margin-bottom: 15px;\n    overflow-y: hidden;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    border: 1px solid #ddd;\n  }\n  .table-responsive > .table {\n    margin-bottom: 0;\n  }\n  .table-responsive > .table > thead > tr > th,\n  .table-responsive > .table > tbody > tr > th,\n  .table-responsive > .table > tfoot > tr > th,\n  .table-responsive > .table > thead > tr > td,\n  .table-responsive > .table > tbody > tr > td,\n  .table-responsive > .table > tfoot > tr > td {\n    white-space: nowrap;\n  }\n  .table-responsive > .table-bordered {\n    border: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:first-child,\n  .table-responsive > .table-bordered > tbody > tr > th:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n  .table-responsive > .table-bordered > thead > tr > td:first-child,\n  .table-responsive > .table-bordered > tbody > tr > td:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n    border-left: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:last-child,\n  .table-responsive > .table-bordered > tbody > tr > th:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n  .table-responsive > .table-bordered > thead > tr > td:last-child,\n  .table-responsive > .table-bordered > tbody > tr > td:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n    border-right: 0;\n  }\n  .table-responsive > .table-bordered > tbody > tr:last-child > th,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n  .table-responsive > .table-bordered > tbody > tr:last-child > td,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n    border-bottom: 0;\n  }\n}\nfieldset {\n  min-width: 0;\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 20px;\n  font-size: 21px;\n  line-height: inherit;\n  color: #333;\n  border: 0;\n  border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n  display: inline-block;\n  max-width: 100%;\n  margin-bottom: 5px;\n  font-weight: bold;\n}\ninput[type=\"search\"] {\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9;\n  line-height: normal;\n}\ninput[type=\"file\"] {\n  display: block;\n}\ninput[type=\"range\"] {\n  display: block;\n  width: 100%;\n}\nselect[multiple],\nselect[size] {\n  height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\noutput {\n  display: block;\n  padding-top: 7px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #555;\n}\n.form-control {\n  display: block;\n  width: 100%;\n  height: 34px;\n  padding: 6px 12px;\n  font-size: 14px;\n  line-height: 1.42857143;\n  color: #555;\n  background-color: #fff;\n  background-image: none;\n  border: 1px solid #ccc;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n  -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;\n       -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n          transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n  border-color: #66afe9;\n  outline: 0;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);\n          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);\n}\n.form-control::-moz-placeholder {\n  color: #999;\n  opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n  color: #999;\n}\n.form-control::-webkit-input-placeholder {\n  color: #999;\n}\n.form-control::-ms-expand {\n  background-color: transparent;\n  border: 0;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n  background-color: #eee;\n  opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n  cursor: not-allowed;\n}\ntextarea.form-control {\n  height: auto;\n}\ninput[type=\"search\"] {\n  -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n  input[type=\"date\"].form-control,\n  input[type=\"time\"].form-control,\n  input[type=\"datetime-local\"].form-control,\n  input[type=\"month\"].form-control {\n    line-height: 34px;\n  }\n  input[type=\"date\"].input-sm,\n  input[type=\"time\"].input-sm,\n  input[type=\"datetime-local\"].input-sm,\n  input[type=\"month\"].input-sm,\n  .input-group-sm input[type=\"date\"],\n  .input-group-sm input[type=\"time\"],\n  .input-group-sm input[type=\"datetime-local\"],\n  .input-group-sm input[type=\"month\"] {\n    line-height: 30px;\n  }\n  input[type=\"date\"].input-lg,\n  input[type=\"time\"].input-lg,\n  input[type=\"datetime-local\"].input-lg,\n  input[type=\"month\"].input-lg,\n  .input-group-lg input[type=\"date\"],\n  .input-group-lg input[type=\"time\"],\n  .input-group-lg input[type=\"datetime-local\"],\n  .input-group-lg input[type=\"month\"] {\n    line-height: 46px;\n  }\n}\n.form-group {\n  margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n  position: relative;\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n  min-height: 20px;\n  padding-left: 20px;\n  margin-bottom: 0;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  position: absolute;\n  margin-top: 4px \\9;\n  margin-left: -20px;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n  position: relative;\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  font-weight: normal;\n  vertical-align: middle;\n  cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n  cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n  cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n  cursor: not-allowed;\n}\n.form-control-static {\n  min-height: 34px;\n  padding-top: 7px;\n  padding-bottom: 7px;\n  margin-bottom: 0;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n  padding-right: 0;\n  padding-left: 0;\n}\n.input-sm {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\nselect.input-sm {\n  height: 30px;\n  line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n  height: auto;\n}\n.form-group-sm .form-control {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.form-group-sm select.form-control {\n  height: 30px;\n  line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n  height: auto;\n}\n.form-group-sm .form-control-static {\n  height: 30px;\n  min-height: 32px;\n  padding: 6px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n}\n.input-lg {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\nselect.input-lg {\n  height: 46px;\n  line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n  height: auto;\n}\n.form-group-lg .form-control {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\n.form-group-lg select.form-control {\n  height: 46px;\n  line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n  height: auto;\n}\n.form-group-lg .form-control-static {\n  height: 46px;\n  min-height: 38px;\n  padding: 11px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n}\n.has-feedback {\n  position: relative;\n}\n.has-feedback .form-control {\n  padding-right: 42.5px;\n}\n.form-control-feedback {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2;\n  display: block;\n  width: 34px;\n  height: 34px;\n  line-height: 34px;\n  text-align: center;\n  pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n  width: 46px;\n  height: 46px;\n  line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n  width: 30px;\n  height: 30px;\n  line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n  color: #3c763d;\n}\n.has-success .form-control {\n  border-color: #3c763d;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n}\n.has-success .form-control:focus {\n  border-color: #2b542c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n  color: #3c763d;\n  background-color: #dff0d8;\n  border-color: #3c763d;\n}\n.has-success .form-control-feedback {\n  color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n  color: #8a6d3b;\n}\n.has-warning .form-control {\n  border-color: #8a6d3b;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n}\n.has-warning .form-control:focus {\n  border-color: #66512c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n  border-color: #8a6d3b;\n}\n.has-warning .form-control-feedback {\n  color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n  color: #a94442;\n}\n.has-error .form-control {\n  border-color: #a94442;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n}\n.has-error .form-control:focus {\n  border-color: #843534;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n  color: #a94442;\n  background-color: #f2dede;\n  border-color: #a94442;\n}\n.has-error .form-control-feedback {\n  color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n  top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n  top: 0;\n}\n.help-block {\n  display: block;\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: #737373;\n}\n@media (min-width: 768px) {\n  .form-inline .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .form-inline .form-control-static {\n    display: inline-block;\n  }\n  .form-inline .input-group {\n    display: inline-table;\n    vertical-align: middle;\n  }\n  .form-inline .input-group .input-group-addon,\n  .form-inline .input-group .input-group-btn,\n  .form-inline .input-group .form-control {\n    width: auto;\n  }\n  .form-inline .input-group > .form-control {\n    width: 100%;\n  }\n  .form-inline .control-label {\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .radio,\n  .form-inline .checkbox {\n    display: inline-block;\n    margin-top: 0;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .radio label,\n  .form-inline .checkbox label {\n    padding-left: 0;\n  }\n  .form-inline .radio input[type=\"radio\"],\n  .form-inline .checkbox input[type=\"checkbox\"] {\n    position: relative;\n    margin-left: 0;\n  }\n  .form-inline .has-feedback .form-control-feedback {\n    top: 0;\n  }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n  padding-top: 7px;\n  margin-top: 0;\n  margin-bottom: 0;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n  min-height: 27px;\n}\n.form-horizontal .form-group {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n@media (min-width: 768px) {\n  .form-horizontal .control-label {\n    padding-top: 7px;\n    margin-bottom: 0;\n    text-align: right;\n  }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n  right: 15px;\n}\n@media (min-width: 768px) {\n  .form-horizontal .form-group-lg .control-label {\n    padding-top: 11px;\n    font-size: 18px;\n  }\n}\n@media (min-width: 768px) {\n  .form-horizontal .form-group-sm .control-label {\n    padding-top: 6px;\n    font-size: 12px;\n  }\n}\n.btn {\n  display: inline-block;\n  padding: 6px 12px;\n  margin-bottom: 0;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1.42857143;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: middle;\n  -ms-touch-action: manipulation;\n      touch-action: manipulation;\n  cursor: pointer;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n      -ms-user-select: none;\n          user-select: none;\n  background-image: none;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n  color: #333;\n  text-decoration: none;\n}\n.btn:active,\n.btn.active {\n  background-image: none;\n  outline: 0;\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n  cursor: not-allowed;\n  filter: alpha(opacity=65);\n  -webkit-box-shadow: none;\n          box-shadow: none;\n  opacity: .65;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n  pointer-events: none;\n}\n.btn-default {\n  color: #333;\n  background-color: #fff;\n  border-color: #ccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n  color: #333;\n  background-color: #e6e6e6;\n  border-color: #8c8c8c;\n}\n.btn-default:hover {\n  color: #333;\n  background-color: #e6e6e6;\n  border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n  color: #333;\n  background-color: #e6e6e6;\n  border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n  color: #333;\n  background-color: #d4d4d4;\n  border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n  background-image: none;\n}\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus {\n  background-color: #fff;\n  border-color: #ccc;\n}\n.btn-default .badge {\n  color: #fff;\n  background-color: #333;\n}\n.btn-primary {\n  color: #fff;\n  background-color: #337ab7;\n  border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n  color: #fff;\n  background-color: #286090;\n  border-color: #122b40;\n}\n.btn-primary:hover {\n  color: #fff;\n  background-color: #286090;\n  border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n  color: #fff;\n  background-color: #286090;\n  border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n  color: #fff;\n  background-color: #204d74;\n  border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n  background-image: none;\n}\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus {\n  background-color: #337ab7;\n  border-color: #2e6da4;\n}\n.btn-primary .badge {\n  color: #337ab7;\n  background-color: #fff;\n}\n.btn-success {\n  color: #fff;\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n  color: #fff;\n  background-color: #449d44;\n  border-color: #255625;\n}\n.btn-success:hover {\n  color: #fff;\n  background-color: #449d44;\n  border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n  color: #fff;\n  background-color: #449d44;\n  border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n  color: #fff;\n  background-color: #398439;\n  border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n  background-image: none;\n}\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus {\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n.btn-success .badge {\n  color: #5cb85c;\n  background-color: #fff;\n}\n.btn-info {\n  color: #fff;\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n  color: #fff;\n  background-color: #31b0d5;\n  border-color: #1b6d85;\n}\n.btn-info:hover {\n  color: #fff;\n  background-color: #31b0d5;\n  border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n  color: #fff;\n  background-color: #31b0d5;\n  border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n  color: #fff;\n  background-color: #269abc;\n  border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n  background-image: none;\n}\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus {\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n.btn-info .badge {\n  color: #5bc0de;\n  background-color: #fff;\n}\n.btn-warning {\n  color: #fff;\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n  color: #fff;\n  background-color: #ec971f;\n  border-color: #985f0d;\n}\n.btn-warning:hover {\n  color: #fff;\n  background-color: #ec971f;\n  border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n  color: #fff;\n  background-color: #ec971f;\n  border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n  color: #fff;\n  background-color: #d58512;\n  border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n  background-image: none;\n}\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus {\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n.btn-warning .badge {\n  color: #f0ad4e;\n  background-color: #fff;\n}\n.btn-danger {\n  color: #fff;\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n  color: #fff;\n  background-color: #c9302c;\n  border-color: #761c19;\n}\n.btn-danger:hover {\n  color: #fff;\n  background-color: #c9302c;\n  border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n  color: #fff;\n  background-color: #c9302c;\n  border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n  color: #fff;\n  background-color: #ac2925;\n  border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n  background-image: none;\n}\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus {\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n.btn-danger .badge {\n  color: #d9534f;\n  background-color: #fff;\n}\n.btn-link {\n  font-weight: normal;\n  color: #337ab7;\n  border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n  background-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n  border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n  color: #23527c;\n  text-decoration: underline;\n  background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n  color: #777;\n  text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n  padding: 1px 5px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n.btn-block {\n  display: block;\n  width: 100%;\n}\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n  width: 100%;\n}\n.fade {\n  opacity: 0;\n  -webkit-transition: opacity .15s linear;\n       -o-transition: opacity .15s linear;\n          transition: opacity .15s linear;\n}\n.fade.in {\n  opacity: 1;\n}\n.collapse {\n  display: none;\n}\n.collapse.in {\n  display: block;\n}\ntr.collapse.in {\n  display: table-row;\n}\ntbody.collapse.in {\n  display: table-row-group;\n}\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  -webkit-transition-timing-function: ease;\n       -o-transition-timing-function: ease;\n          transition-timing-function: ease;\n  -webkit-transition-duration: .35s;\n       -o-transition-duration: .35s;\n          transition-duration: .35s;\n  -webkit-transition-property: height, visibility;\n       -o-transition-property: height, visibility;\n          transition-property: height, visibility;\n}\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top: 4px dashed;\n  border-top: 4px solid \\9;\n  border-right: 4px solid transparent;\n  border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n  position: relative;\n}\n.dropdown-toggle:focus {\n  outline: 0;\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: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0;\n  font-size: 14px;\n  text-align: left;\n  list-style: none;\n  background-color: #fff;\n  -webkit-background-clip: padding-box;\n          background-clip: padding-box;\n  border: 1px solid #ccc;\n  border: 1px solid rgba(0, 0, 0, .15);\n  border-radius: 4px;\n  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n          box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n}\n.dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n.dropdown-menu .divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n  display: block;\n  padding: 3px 20px;\n  clear: both;\n  font-weight: normal;\n  line-height: 1.42857143;\n  color: #333;\n  white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  color: #262626;\n  text-decoration: none;\n  background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  color: #fff;\n  text-decoration: none;\n  background-color: #337ab7;\n  outline: 0;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  color: #777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  text-decoration: none;\n  cursor: not-allowed;\n  background-color: transparent;\n  background-image: none;\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n}\n.open > .dropdown-menu {\n  display: block;\n}\n.open > a {\n  outline: 0;\n}\n.dropdown-menu-right {\n  right: 0;\n  left: auto;\n}\n.dropdown-menu-left {\n  right: auto;\n  left: 0;\n}\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: 12px;\n  line-height: 1.42857143;\n  color: #777;\n  white-space: nowrap;\n}\n.dropdown-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 990;\n}\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n  content: \"\";\n  border-top: 0;\n  border-bottom: 4px dashed;\n  border-bottom: 4px solid \\9;\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n  .navbar-right .dropdown-menu {\n    right: 0;\n    left: auto;\n  }\n  .navbar-right .dropdown-menu-left {\n    right: auto;\n    left: 0;\n  }\n}\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n  z-index: 2;\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  margin-left: -1px;\n}\n.btn-toolbar {\n  margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n  float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n  margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n.btn-group > .btn:first-child {\n  margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group > .btn-group {\n  float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n  padding-right: 8px;\n  padding-left: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-right: 12px;\n  padding-left: 12px;\n}\n.btn-group.open .dropdown-toggle {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n.btn .caret {\n  margin-left: 0;\n}\n.btn-lg .caret {\n  border-width: 5px 5px 0;\n  border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n  border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n  display: block;\n  float: none;\n  width: 100%;\n  max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n  float: none;\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.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n  border-top-left-radius: 4px;\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  table-layout: fixed;\n  border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n  display: table-cell;\n  float: none;\n  width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n  width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n  left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n  position: absolute;\n  clip: rect(0, 0, 0, 0);\n  pointer-events: none;\n}\n.input-group {\n  position: relative;\n  display: table;\n  border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n  float: none;\n  padding-right: 0;\n  padding-left: 0;\n}\n.input-group .form-control {\n  position: relative;\n  z-index: 2;\n  float: left;\n  width: 100%;\n  margin-bottom: 0;\n}\n.input-group .form-control:focus {\n  z-index: 3;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n  border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n  height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n  height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle;\n}\n.input-group-addon {\n  padding: 6px 12px;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1;\n  color: #555;\n  text-align: center;\n  background-color: #eee;\n  border: 1px solid #ccc;\n  border-radius: 4px;\n}\n.input-group-addon.input-sm {\n  padding: 5px 10px;\n  font-size: 12px;\n  border-radius: 3px;\n}\n.input-group-addon.input-lg {\n  padding: 10px 16px;\n  font-size: 18px;\n  border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n  margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n.input-group-addon:first-child {\n  border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.input-group-addon:last-child {\n  border-left: 0;\n}\n.input-group-btn {\n  position: relative;\n  font-size: 0;\n  white-space: nowrap;\n}\n.input-group-btn > .btn {\n  position: relative;\n}\n.input-group-btn > .btn + .btn {\n  margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n  z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n  margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n  z-index: 2;\n  margin-left: -1px;\n}\n.nav {\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n.nav > li {\n  position: relative;\n  display: block;\n}\n.nav > li > a {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n  text-decoration: none;\n  background-color: #eee;\n}\n.nav > li.disabled > a {\n  color: #777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n  color: #777;\n  text-decoration: none;\n  cursor: not-allowed;\n  background-color: transparent;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n  background-color: #eee;\n  border-color: #337ab7;\n}\n.nav .nav-divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n.nav > li > a > img {\n  max-width: none;\n}\n.nav-tabs {\n  border-bottom: 1px solid #ddd;\n}\n.nav-tabs > li {\n  float: left;\n  margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n  margin-right: 2px;\n  line-height: 1.42857143;\n  border: 1px solid transparent;\n  border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n  border-color: #eee #eee #ddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n  color: #555;\n  cursor: default;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  border-bottom-color: transparent;\n}\n.nav-tabs.nav-justified {\n  width: 100%;\n  border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n  float: none;\n}\n.nav-tabs.nav-justified > li > a {\n  margin-bottom: 5px;\n  text-align: center;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-tabs.nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n.nav-tabs.nav-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n  border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li > a {\n    border-bottom: 1px solid #ddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs.nav-justified > .active > a,\n  .nav-tabs.nav-justified > .active > a:hover,\n  .nav-tabs.nav-justified > .active > a:focus {\n    border-bottom-color: #fff;\n  }\n}\n.nav-pills > li {\n  float: left;\n}\n.nav-pills > li > a {\n  border-radius: 4px;\n}\n.nav-pills > li + li {\n  margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n  color: #fff;\n  background-color: #337ab7;\n}\n.nav-stacked > li {\n  float: none;\n}\n.nav-stacked > li + li {\n  margin-top: 2px;\n  margin-left: 0;\n}\n.nav-justified {\n  width: 100%;\n}\n.nav-justified > li {\n  float: none;\n}\n.nav-justified > li > a {\n  margin-bottom: 5px;\n  text-align: center;\n}\n.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n@media (min-width: 768px) {\n  .nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n.nav-tabs-justified {\n  border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n  border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n  .nav-tabs-justified > li > a {\n    border-bottom: 1px solid #ddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs-justified > .active > a,\n  .nav-tabs-justified > .active > a:hover,\n  .nav-tabs-justified > .active > a:focus {\n    border-bottom-color: #fff;\n  }\n}\n.tab-content > .tab-pane {\n  display: none;\n}\n.tab-content > .active {\n  display: block;\n}\n.nav-tabs .dropdown-menu {\n  margin-top: -1px;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n.navbar {\n  position: relative;\n  min-height: 50px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n  .navbar {\n    border-radius: 4px;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-header {\n    float: left;\n  }\n}\n.navbar-collapse {\n  padding-right: 15px;\n  padding-left: 15px;\n  overflow-x: visible;\n  -webkit-overflow-scrolling: touch;\n  border-top: 1px solid transparent;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);\n}\n.navbar-collapse.in {\n  overflow-y: auto;\n}\n@media (min-width: 768px) {\n  .navbar-collapse {\n    width: auto;\n    border-top: 0;\n    -webkit-box-shadow: none;\n            box-shadow: none;\n  }\n  .navbar-collapse.collapse {\n    display: block !important;\n    height: auto !important;\n    padding-bottom: 0;\n    overflow: visible !important;\n  }\n  .navbar-collapse.in {\n    overflow-y: visible;\n  }\n  .navbar-fixed-top .navbar-collapse,\n  .navbar-static-top .navbar-collapse,\n  .navbar-fixed-bottom .navbar-collapse {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n  max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n  .navbar-fixed-top .navbar-collapse,\n  .navbar-fixed-bottom .navbar-collapse {\n    max-height: 200px;\n  }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n@media (min-width: 768px) {\n  .container > .navbar-header,\n  .container-fluid > .navbar-header,\n  .container > .navbar-collapse,\n  .container-fluid > .navbar-collapse {\n    margin-right: 0;\n    margin-left: 0;\n  }\n}\n.navbar-static-top {\n  z-index: 1000;\n  border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n  .navbar-static-top {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n@media (min-width: 768px) {\n  .navbar-fixed-top,\n  .navbar-fixed-bottom {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0;\n  border-width: 1px 0 0;\n}\n.navbar-brand {\n  float: left;\n  height: 50px;\n  padding: 15px 15px;\n  font-size: 18px;\n  line-height: 20px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n  text-decoration: none;\n}\n.navbar-brand > img {\n  display: block;\n}\n@media (min-width: 768px) {\n  .navbar > .container .navbar-brand,\n  .navbar > .container-fluid .navbar-brand {\n    margin-left: -15px;\n  }\n}\n.navbar-toggle {\n  position: relative;\n  float: right;\n  padding: 9px 10px;\n  margin-top: 8px;\n  margin-right: 15px;\n  margin-bottom: 8px;\n  background-color: transparent;\n  background-image: none;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n.navbar-toggle:focus {\n  outline: 0;\n}\n.navbar-toggle .icon-bar {\n  display: block;\n  width: 22px;\n  height: 2px;\n  border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n  margin-top: 4px;\n}\n@media (min-width: 768px) {\n  .navbar-toggle {\n    display: none;\n  }\n}\n.navbar-nav {\n  margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n  padding-top: 10px;\n  padding-bottom: 10px;\n  line-height: 20px;\n}\n@media (max-width: 767px) {\n  .navbar-nav .open .dropdown-menu {\n    position: static;\n    float: none;\n    width: auto;\n    margin-top: 0;\n    background-color: transparent;\n    border: 0;\n    -webkit-box-shadow: none;\n            box-shadow: none;\n  }\n  .navbar-nav .open .dropdown-menu > li > a,\n  .navbar-nav .open .dropdown-menu .dropdown-header {\n    padding: 5px 15px 5px 25px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a {\n    line-height: 20px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-nav .open .dropdown-menu > li > a:focus {\n    background-image: none;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-nav {\n    float: left;\n    margin: 0;\n  }\n  .navbar-nav > li {\n    float: left;\n  }\n  .navbar-nav > li > a {\n    padding-top: 15px;\n    padding-bottom: 15px;\n  }\n}\n.navbar-form {\n  padding: 10px 15px;\n  margin-top: 8px;\n  margin-right: -15px;\n  margin-bottom: 8px;\n  margin-left: -15px;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);\n}\n@media (min-width: 768px) {\n  .navbar-form .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control {\n    display: inline-block;\n    width: auto;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control-static {\n    display: inline-block;\n  }\n  .navbar-form .input-group {\n    display: inline-table;\n    vertical-align: middle;\n  }\n  .navbar-form .input-group .input-group-addon,\n  .navbar-form .input-group .input-group-btn,\n  .navbar-form .input-group .form-control {\n    width: auto;\n  }\n  .navbar-form .input-group > .form-control {\n    width: 100%;\n  }\n  .navbar-form .control-label {\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .radio,\n  .navbar-form .checkbox {\n    display: inline-block;\n    margin-top: 0;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .radio label,\n  .navbar-form .checkbox label {\n    padding-left: 0;\n  }\n  .navbar-form .radio input[type=\"radio\"],\n  .navbar-form .checkbox input[type=\"checkbox\"] {\n    position: relative;\n    margin-left: 0;\n  }\n  .navbar-form .has-feedback .form-control-feedback {\n    top: 0;\n  }\n}\n@media (max-width: 767px) {\n  .navbar-form .form-group {\n    margin-bottom: 5px;\n  }\n  .navbar-form .form-group:last-child {\n    margin-bottom: 0;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-form {\n    width: auto;\n    padding-top: 0;\n    padding-bottom: 0;\n    margin-right: 0;\n    margin-left: 0;\n    border: 0;\n    -webkit-box-shadow: none;\n            box-shadow: none;\n  }\n}\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  margin-bottom: 0;\n  border-top-left-radius: 4px;\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n.navbar-btn {\n  margin-top: 8px;\n  margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n  margin-top: 14px;\n  margin-bottom: 14px;\n}\n.navbar-text {\n  margin-top: 15px;\n  margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n  .navbar-text {\n    float: left;\n    margin-right: 15px;\n    margin-left: 15px;\n  }\n}\n@media (min-width: 768px) {\n  .navbar-left {\n    float: left !important;\n  }\n  .navbar-right {\n    float: right !important;\n    margin-right: -15px;\n  }\n  .navbar-right ~ .navbar-right {\n    margin-right: 0;\n  }\n}\n.navbar-default {\n  background-color: #f8f8f8;\n  border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n  color: #777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n  color: #5e5e5e;\n  background-color: transparent;\n}\n.navbar-default .navbar-text {\n  color: #777;\n}\n.navbar-default .navbar-nav > li > a {\n  color: #777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n  color: #333;\n  background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n  color: #555;\n  background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n  color: #ccc;\n  background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n  border-color: #ddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n  background-color: #ddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n  background-color: #888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n  border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n  color: #555;\n  background-color: #e7e7e7;\n}\n@media (max-width: 767px) {\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n    color: #777;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #333;\n    background-color: transparent;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #555;\n    background-color: #e7e7e7;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #ccc;\n    background-color: transparent;\n  }\n}\n.navbar-default .navbar-link {\n  color: #777;\n}\n.navbar-default .navbar-link:hover {\n  color: #333;\n}\n.navbar-default .btn-link {\n  color: #777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n  color: #333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n  color: #ccc;\n}\n.navbar-inverse {\n  background-color: #222;\n  border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n  color: #fff;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n  color: #fff;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n  color: #fff;\n  background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n  color: #444;\n  background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n  border-color: #333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n  background-color: #333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n  background-color: #fff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n  border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n  color: #fff;\n  background-color: #080808;\n}\n@media (max-width: 767px) {\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n    border-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n    color: #9d9d9d;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #fff;\n    background-color: transparent;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #fff;\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #444;\n    background-color: transparent;\n  }\n}\n.navbar-inverse .navbar-link {\n  color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n  color: #fff;\n}\n.navbar-inverse .btn-link {\n  color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n  color: #fff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n  color: #444;\n}\n.breadcrumb {\n  padding: 8px 15px;\n  margin-bottom: 20px;\n  list-style: none;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n}\n.breadcrumb > li {\n  display: inline-block;\n}\n.breadcrumb > li + li:before {\n  padding: 0 5px;\n  color: #ccc;\n  content: \"/\\00a0\";\n}\n.breadcrumb > .active {\n  color: #777;\n}\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: 20px 0;\n  border-radius: 4px;\n}\n.pagination > li {\n  display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n  position: relative;\n  float: left;\n  padding: 6px 12px;\n  margin-left: -1px;\n  line-height: 1.42857143;\n  color: #337ab7;\n  text-decoration: none;\n  background-color: #fff;\n  border: 1px solid #ddd;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n  margin-left: 0;\n  border-top-left-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n  z-index: 2;\n  color: #23527c;\n  background-color: #eee;\n  border-color: #ddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n  z-index: 3;\n  color: #fff;\n  cursor: default;\n  background-color: #337ab7;\n  border-color: #337ab7;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n  color: #777;\n  cursor: not-allowed;\n  background-color: #fff;\n  border-color: #ddd;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n  border-top-left-radius: 6px;\n  border-bottom-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n  border-top-right-radius: 6px;\n  border-bottom-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n  border-top-left-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n  border-top-right-radius: 3px;\n  border-bottom-right-radius: 3px;\n}\n.pager {\n  padding-left: 0;\n  margin: 20px 0;\n  text-align: center;\n  list-style: none;\n}\n.pager li {\n  display: inline;\n}\n.pager li > a,\n.pager li > span {\n  display: inline-block;\n  padding: 5px 14px;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n  text-decoration: none;\n  background-color: #eee;\n}\n.pager .next > a,\n.pager .next > span {\n  float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n  float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n  color: #777;\n  cursor: not-allowed;\n  background-color: #fff;\n}\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: #fff;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n  color: #fff;\n  text-decoration: none;\n  cursor: pointer;\n}\n.label:empty {\n  display: none;\n}\n.btn .label {\n  position: relative;\n  top: -1px;\n}\n.label-default {\n  background-color: #777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n  background-color: #5e5e5e;\n}\n.label-primary {\n  background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n  background-color: #286090;\n}\n.label-success {\n  background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n  background-color: #449d44;\n}\n.label-info {\n  background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n  background-color: #31b0d5;\n}\n.label-warning {\n  background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n  background-color: #ec971f;\n}\n.label-danger {\n  background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n  background-color: #c9302c;\n}\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: 12px;\n  font-weight: bold;\n  line-height: 1;\n  color: #fff;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: middle;\n  background-color: #777;\n  border-radius: 10px;\n}\n.badge:empty {\n  display: none;\n}\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n  top: 0;\n  padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n  color: #fff;\n  text-decoration: none;\n  cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n  color: #337ab7;\n  background-color: #fff;\n}\n.list-group-item > .badge {\n  float: right;\n}\n.list-group-item > .badge + .badge {\n  margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n  margin-left: 3px;\n}\n.jumbotron {\n  padding-top: 30px;\n  padding-bottom: 30px;\n  margin-bottom: 30px;\n  color: inherit;\n  background-color: #eee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n  color: inherit;\n}\n.jumbotron p {\n  margin-bottom: 15px;\n  font-size: 21px;\n  font-weight: 200;\n}\n.jumbotron > hr {\n  border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n  padding-right: 15px;\n  padding-left: 15px;\n  border-radius: 6px;\n}\n.jumbotron .container {\n  max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n  .jumbotron {\n    padding-top: 48px;\n    padding-bottom: 48px;\n  }\n  .container .jumbotron,\n  .container-fluid .jumbotron {\n    padding-right: 60px;\n    padding-left: 60px;\n  }\n  .jumbotron h1,\n  .jumbotron .h1 {\n    font-size: 63px;\n  }\n}\n.thumbnail {\n  display: block;\n  padding: 4px;\n  margin-bottom: 20px;\n  line-height: 1.42857143;\n  background-color: #fff;\n  border: 1px solid #ddd;\n  border-radius: 4px;\n  -webkit-transition: border .2s ease-in-out;\n       -o-transition: border .2s ease-in-out;\n          transition: border .2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n  margin-right: auto;\n  margin-left: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n  border-color: #337ab7;\n}\n.thumbnail .caption {\n  padding: 9px;\n  color: #333;\n}\n.alert {\n  padding: 15px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n.alert h4 {\n  margin-top: 0;\n  color: inherit;\n}\n.alert .alert-link {\n  font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n  margin-bottom: 0;\n}\n.alert > p + p {\n  margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n  padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n  position: relative;\n  top: -2px;\n  right: -21px;\n  color: inherit;\n}\n.alert-success {\n  color: #3c763d;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n.alert-success hr {\n  border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n  color: #2b542c;\n}\n.alert-info {\n  color: #31708f;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n.alert-info hr {\n  border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n  color: #245269;\n}\n.alert-warning {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n}\n.alert-warning hr {\n  border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n  color: #66512c;\n}\n.alert-danger {\n  color: #a94442;\n  background-color: #f2dede;\n  border-color: #ebccd1;\n}\n.alert-danger hr {\n  border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n  color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n@-o-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n.progress {\n  height: 20px;\n  margin-bottom: 20px;\n  overflow: hidden;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n          box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n}\n.progress-bar {\n  float: left;\n  width: 0;\n  height: 100%;\n  font-size: 12px;\n  line-height: 20px;\n  color: #fff;\n  text-align: center;\n  background-color: #337ab7;\n  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);\n          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);\n  -webkit-transition: width .6s ease;\n       -o-transition: width .6s ease;\n          transition: width .6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n  background-image: -webkit-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);\n  background-image:      -o-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);\n  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);\n  -webkit-background-size: 40px 40px;\n          background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n  -webkit-animation: progress-bar-stripes 2s linear infinite;\n       -o-animation: progress-bar-stripes 2s linear infinite;\n          animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n  background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n  background-image: -webkit-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);\n  background-image:      -o-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);\n  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);\n}\n.progress-bar-info {\n  background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n  background-image: -webkit-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);\n  background-image:      -o-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);\n  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);\n}\n.progress-bar-warning {\n  background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n  background-image: -webkit-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);\n  background-image:      -o-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);\n  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);\n}\n.progress-bar-danger {\n  background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n  background-image: -webkit-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);\n  background-image:      -o-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);\n  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);\n}\n.media {\n  margin-top: 15px;\n}\n.media:first-child {\n  margin-top: 0;\n}\n.media,\n.media-body {\n  overflow: hidden;\n  zoom: 1;\n}\n.media-body {\n  width: 10000px;\n}\n.media-object {\n  display: block;\n}\n.media-object.img-thumbnail {\n  max-width: none;\n}\n.media-right,\n.media > .pull-right {\n  padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n  padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n  display: table-cell;\n  vertical-align: top;\n}\n.media-middle {\n  vertical-align: middle;\n}\n.media-bottom {\n  vertical-align: bottom;\n}\n.media-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n.list-group {\n  padding-left: 0;\n  margin-bottom: 20px;\n}\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  margin-bottom: -1px;\n  background-color: #fff;\n  border: 1px solid #ddd;\n}\n.list-group-item:first-child {\n  border-top-left-radius: 4px;\n  border-top-right-radius: 4px;\n}\n.list-group-item:last-child {\n  margin-bottom: 0;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n  color: #555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n  color: #333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n  color: #555;\n  text-decoration: none;\n  background-color: #f5f5f5;\n}\nbutton.list-group-item {\n  width: 100%;\n  text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n  color: #777;\n  cursor: not-allowed;\n  background-color: #eee;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n  color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n  color: #777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  z-index: 2;\n  color: #fff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n  color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n  color: #c7ddef;\n}\n.list-group-item-success {\n  color: #3c763d;\n  background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n  color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n  color: #3c763d;\n  background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n  color: #fff;\n  background-color: #3c763d;\n  border-color: #3c763d;\n}\n.list-group-item-info {\n  color: #31708f;\n  background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n  color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n  color: #31708f;\n  background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n  color: #fff;\n  background-color: #31708f;\n  border-color: #31708f;\n}\n.list-group-item-warning {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n  color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n  color: #8a6d3b;\n  background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n  color: #fff;\n  background-color: #8a6d3b;\n  border-color: #8a6d3b;\n}\n.list-group-item-danger {\n  color: #a94442;\n  background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n  color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n  color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n  color: #a94442;\n  background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n  color: #fff;\n  background-color: #a94442;\n  border-color: #a94442;\n}\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n.panel {\n  margin-bottom: 20px;\n  background-color: #fff;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);\n          box-shadow: 0 1px 1px rgba(0, 0, 0, .05);\n}\n.panel-body {\n  padding: 15px;\n}\n.panel-heading {\n  padding: 10px 15px;\n  border-bottom: 1px solid transparent;\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n  color: inherit;\n}\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: 16px;\n  color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n  color: inherit;\n}\n.panel-footer {\n  padding: 10px 15px;\n  background-color: #f5f5f5;\n  border-top: 1px solid #ddd;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n  margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n  border-width: 1px 0;\n  border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n  border-top: 0;\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n  border-bottom: 0;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n  border-top-width: 0;\n}\n.list-group + .panel-footer {\n  border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n  margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n  padding-right: 15px;\n  padding-left: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n  border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n  border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n  border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n  border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n  border-top: 1px solid #ddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n  border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n  border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n  border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n  border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n  border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n  border-bottom: 0;\n}\n.panel > .table-responsive {\n  margin-bottom: 0;\n  border: 0;\n}\n.panel-group {\n  margin-bottom: 20px;\n}\n.panel-group .panel {\n  margin-bottom: 0;\n  border-radius: 4px;\n}\n.panel-group .panel + .panel {\n  margin-top: 5px;\n}\n.panel-group .panel-heading {\n  border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n  border-top: 1px solid #ddd;\n}\n.panel-group .panel-footer {\n  border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n  border-bottom: 1px solid #ddd;\n}\n.panel-default {\n  border-color: #ddd;\n}\n.panel-default > .panel-heading {\n  color: #333;\n  background-color: #f5f5f5;\n  border-color: #ddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #ddd;\n}\n.panel-default > .panel-heading .badge {\n  color: #f5f5f5;\n  background-color: #333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #ddd;\n}\n.panel-primary {\n  border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n  color: #fff;\n  background-color: #337ab7;\n  border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n  color: #337ab7;\n  background-color: #fff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #337ab7;\n}\n.panel-success {\n  border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n  color: #3c763d;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n  color: #dff0d8;\n  background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #d6e9c6;\n}\n.panel-info {\n  border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n  color: #31708f;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n  color: #d9edf7;\n  background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #bce8f1;\n}\n.panel-warning {\n  border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n  color: #fcf8e3;\n  background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #faebcc;\n}\n.panel-danger {\n  border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n  color: #a94442;\n  background-color: #f2dede;\n  border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n  border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n  color: #f2dede;\n  background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n  border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n  position: relative;\n  display: block;\n  height: 0;\n  padding: 0;\n  overflow: hidden;\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.embed-responsive-16by9 {\n  padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n  padding-bottom: 75%;\n}\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border: 1px solid #e3e3e3;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);\n}\n.well blockquote {\n  border-color: #ddd;\n  border-color: rgba(0, 0, 0, .15);\n}\n.well-lg {\n  padding: 24px;\n  border-radius: 6px;\n}\n.well-sm {\n  padding: 9px;\n  border-radius: 3px;\n}\n.close {\n  float: right;\n  font-size: 21px;\n  font-weight: bold;\n  line-height: 1;\n  color: #000;\n  text-shadow: 0 1px 0 #fff;\n  filter: alpha(opacity=20);\n  opacity: .2;\n}\n.close:hover,\n.close:focus {\n  color: #000;\n  text-decoration: none;\n  cursor: pointer;\n  filter: alpha(opacity=50);\n  opacity: .5;\n}\nbutton.close {\n  -webkit-appearance: none;\n  padding: 0;\n  cursor: pointer;\n  background: transparent;\n  border: 0;\n}\n.modal-open {\n  overflow: hidden;\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  -webkit-overflow-scrolling: touch;\n  outline: 0;\n}\n.modal.fade .modal-dialog {\n  -webkit-transition: -webkit-transform .3s ease-out;\n       -o-transition:      -o-transform .3s ease-out;\n          transition:         transform .3s ease-out;\n  -webkit-transform: translate(0, -25%);\n      -ms-transform: translate(0, -25%);\n       -o-transform: translate(0, -25%);\n          transform: translate(0, -25%);\n}\n.modal.in .modal-dialog {\n  -webkit-transform: translate(0, 0);\n      -ms-transform: translate(0, 0);\n       -o-transform: translate(0, 0);\n          transform: translate(0, 0);\n}\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 10px;\n}\n.modal-content {\n  position: relative;\n  background-color: #fff;\n  -webkit-background-clip: padding-box;\n          background-clip: padding-box;\n  border: 1px solid #999;\n  border: 1px solid rgba(0, 0, 0, .2);\n  border-radius: 6px;\n  outline: 0;\n  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);\n          box-shadow: 0 3px 9px rgba(0, 0, 0, .5);\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.modal-backdrop.fade {\n  filter: alpha(opacity=0);\n  opacity: 0;\n}\n.modal-backdrop.in {\n  filter: alpha(opacity=50);\n  opacity: .5;\n}\n.modal-header {\n  padding: 15px;\n  border-bottom: 1px solid #e5e5e5;\n}\n.modal-header .close {\n  margin-top: -2px;\n}\n.modal-title {\n  margin: 0;\n  line-height: 1.42857143;\n}\n.modal-body {\n  position: relative;\n  padding: 15px;\n}\n.modal-footer {\n  padding: 15px;\n  text-align: right;\n  border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n  margin-bottom: 0;\n  margin-left: 5px;\n}\n.modal-footer .btn-group .btn + .btn {\n  margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n  margin-left: 0;\n}\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n@media (min-width: 768px) {\n  .modal-dialog {\n    width: 600px;\n    margin: 30px auto;\n  }\n  .modal-content {\n    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);\n            box-shadow: 0 5px 15px rgba(0, 0, 0, .5);\n  }\n  .modal-sm {\n    width: 300px;\n  }\n}\n@media (min-width: 992px) {\n  .modal-lg {\n    width: 900px;\n  }\n}\n.tooltip {\n  position: absolute;\n  z-index: 1070;\n  display: block;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 12px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.42857143;\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  word-wrap: normal;\n  white-space: normal;\n  filter: alpha(opacity=0);\n  opacity: 0;\n\n  line-break: auto;\n}\n.tooltip.in {\n  filter: alpha(opacity=90);\n  opacity: .9;\n}\n.tooltip.top {\n  padding: 5px 0;\n  margin-top: -3px;\n}\n.tooltip.right {\n  padding: 0 5px;\n  margin-left: 3px;\n}\n.tooltip.bottom {\n  padding: 5px 0;\n  margin-top: 3px;\n}\n.tooltip.left {\n  padding: 0 5px;\n  margin-left: -3px;\n}\n.tooltip-inner {\n  max-width: 200px;\n  padding: 3px 8px;\n  color: #fff;\n  text-align: center;\n  background-color: #000;\n  border-radius: 4px;\n}\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n  bottom: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000;\n}\n.tooltip.top-left .tooltip-arrow {\n  right: 5px;\n  bottom: 0;\n  margin-bottom: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000;\n}\n.tooltip.top-right .tooltip-arrow {\n  bottom: 0;\n  left: 5px;\n  margin-bottom: -5px;\n  border-width: 5px 5px 0;\n  border-top-color: #000;\n}\n.tooltip.right .tooltip-arrow {\n  top: 50%;\n  left: 0;\n  margin-top: -5px;\n  border-width: 5px 5px 5px 0;\n  border-right-color: #000;\n}\n.tooltip.left .tooltip-arrow {\n  top: 50%;\n  right: 0;\n  margin-top: -5px;\n  border-width: 5px 0 5px 5px;\n  border-left-color: #000;\n}\n.tooltip.bottom .tooltip-arrow {\n  top: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n  top: 0;\n  right: 5px;\n  margin-top: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n  top: 0;\n  left: 5px;\n  margin-top: -5px;\n  border-width: 0 5px 5px;\n  border-bottom-color: #000;\n}\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1060;\n  display: none;\n  max-width: 276px;\n  padding: 1px;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1.42857143;\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  word-wrap: normal;\n  white-space: normal;\n  background-color: #fff;\n  -webkit-background-clip: padding-box;\n          background-clip: padding-box;\n  border: 1px solid #ccc;\n  border: 1px solid rgba(0, 0, 0, .2);\n  border-radius: 6px;\n  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);\n          box-shadow: 0 5px 10px rgba(0, 0, 0, .2);\n\n  line-break: auto;\n}\n.popover.top {\n  margin-top: -10px;\n}\n.popover.right {\n  margin-left: 10px;\n}\n.popover.bottom {\n  margin-top: 10px;\n}\n.popover.left {\n  margin-left: -10px;\n}\n.popover-title {\n  padding: 8px 14px;\n  margin: 0;\n  font-size: 14px;\n  background-color: #f7f7f7;\n  border-bottom: 1px solid #ebebeb;\n  border-radius: 5px 5px 0 0;\n}\n.popover-content {\n  padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n  position: absolute;\n  display: block;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n.popover > .arrow {\n  border-width: 11px;\n}\n.popover > .arrow:after {\n  content: \"\";\n  border-width: 10px;\n}\n.popover.top > .arrow {\n  bottom: -11px;\n  left: 50%;\n  margin-left: -11px;\n  border-top-color: #999;\n  border-top-color: rgba(0, 0, 0, .25);\n  border-bottom-width: 0;\n}\n.popover.top > .arrow:after {\n  bottom: 1px;\n  margin-left: -10px;\n  content: \" \";\n  border-top-color: #fff;\n  border-bottom-width: 0;\n}\n.popover.right > .arrow {\n  top: 50%;\n  left: -11px;\n  margin-top: -11px;\n  border-right-color: #999;\n  border-right-color: rgba(0, 0, 0, .25);\n  border-left-width: 0;\n}\n.popover.right > .arrow:after {\n  bottom: -10px;\n  left: 1px;\n  content: \" \";\n  border-right-color: #fff;\n  border-left-width: 0;\n}\n.popover.bottom > .arrow {\n  top: -11px;\n  left: 50%;\n  margin-left: -11px;\n  border-top-width: 0;\n  border-bottom-color: #999;\n  border-bottom-color: rgba(0, 0, 0, .25);\n}\n.popover.bottom > .arrow:after {\n  top: 1px;\n  margin-left: -10px;\n  content: \" \";\n  border-top-width: 0;\n  border-bottom-color: #fff;\n}\n.popover.left > .arrow {\n  top: 50%;\n  right: -11px;\n  margin-top: -11px;\n  border-right-width: 0;\n  border-left-color: #999;\n  border-left-color: rgba(0, 0, 0, .25);\n}\n.popover.left > .arrow:after {\n  right: 1px;\n  bottom: -10px;\n  content: \" \";\n  border-right-width: 0;\n  border-left-color: #fff;\n}\n.carousel {\n  position: relative;\n}\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n.carousel-inner > .item {\n  position: relative;\n  display: none;\n  -webkit-transition: .6s ease-in-out left;\n       -o-transition: .6s ease-in-out left;\n          transition: .6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n  .carousel-inner > .item {\n    -webkit-transition: -webkit-transform .6s ease-in-out;\n         -o-transition:      -o-transform .6s ease-in-out;\n            transition:         transform .6s ease-in-out;\n\n    -webkit-backface-visibility: hidden;\n            backface-visibility: hidden;\n    -webkit-perspective: 1000px;\n            perspective: 1000px;\n  }\n  .carousel-inner > .item.next,\n  .carousel-inner > .item.active.right {\n    left: 0;\n    -webkit-transform: translate3d(100%, 0, 0);\n            transform: translate3d(100%, 0, 0);\n  }\n  .carousel-inner > .item.prev,\n  .carousel-inner > .item.active.left {\n    left: 0;\n    -webkit-transform: translate3d(-100%, 0, 0);\n            transform: translate3d(-100%, 0, 0);\n  }\n  .carousel-inner > .item.next.left,\n  .carousel-inner > .item.prev.right,\n  .carousel-inner > .item.active {\n    left: 0;\n    -webkit-transform: translate3d(0, 0, 0);\n            transform: translate3d(0, 0, 0);\n  }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  display: block;\n}\n.carousel-inner > .active {\n  left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n.carousel-inner > .next {\n  left: 100%;\n}\n.carousel-inner > .prev {\n  left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n  left: 0;\n}\n.carousel-inner > .active.left {\n  left: -100%;\n}\n.carousel-inner > .active.right {\n  left: 100%;\n}\n.carousel-control {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  width: 15%;\n  font-size: 20px;\n  color: #fff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);\n  background-color: rgba(0, 0, 0, 0);\n  filter: alpha(opacity=50);\n  opacity: .5;\n}\n.carousel-control.left {\n  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);\n  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);\n  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001)));\n  background-image:         linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n  background-repeat: repeat-x;\n}\n.carousel-control.right {\n  right: 0;\n  left: auto;\n  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);\n  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);\n  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5)));\n  background-image:         linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n  background-repeat: repeat-x;\n}\n.carousel-control:hover,\n.carousel-control:focus {\n  color: #fff;\n  text-decoration: none;\n  filter: alpha(opacity=90);\n  outline: 0;\n  opacity: .9;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n  position: absolute;\n  top: 50%;\n  z-index: 5;\n  display: inline-block;\n  margin-top: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n  left: 50%;\n  margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n  right: 50%;\n  margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n  width: 20px;\n  height: 20px;\n  font-family: serif;\n  line-height: 1;\n}\n.carousel-control .icon-prev:before {\n  content: '\\2039';\n}\n.carousel-control .icon-next:before {\n  content: '\\203a';\n}\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  padding-left: 0;\n  margin-left: -30%;\n  text-align: center;\n  list-style: none;\n}\n.carousel-indicators li {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  margin: 1px;\n  text-indent: -999px;\n  cursor: pointer;\n  background-color: #000 \\9;\n  background-color: rgba(0, 0, 0, 0);\n  border: 1px solid #fff;\n  border-radius: 10px;\n}\n.carousel-indicators .active {\n  width: 12px;\n  height: 12px;\n  margin: 0;\n  background-color: #fff;\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  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);\n}\n.carousel-caption .btn {\n  text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n  .carousel-control .glyphicon-chevron-left,\n  .carousel-control .glyphicon-chevron-right,\n  .carousel-control .icon-prev,\n  .carousel-control .icon-next {\n    width: 30px;\n    height: 30px;\n    margin-top: -10px;\n    font-size: 30px;\n  }\n  .carousel-control .glyphicon-chevron-left,\n  .carousel-control .icon-prev {\n    margin-left: -10px;\n  }\n  .carousel-control .glyphicon-chevron-right,\n  .carousel-control .icon-next {\n    margin-right: -10px;\n  }\n  .carousel-caption {\n    right: 20%;\n    left: 20%;\n    padding-bottom: 30px;\n  }\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-header:before,\n.modal-header:after,\n.modal-footer:before,\n.modal-footer:after {\n  display: table;\n  content: \" \";\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-header:after,\n.modal-footer:after {\n  clear: both;\n}\n.center-block {\n  display: block;\n  margin-right: auto;\n  margin-left: auto;\n}\n.pull-right {\n  float: right !important;\n}\n.pull-left {\n  float: left !important;\n}\n.hide {\n  display: none !important;\n}\n.show {\n  display: block !important;\n}\n.invisible {\n  visibility: hidden;\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.hidden {\n  display: none !important;\n}\n.affix {\n  position: fixed;\n}\n@-ms-viewport {\n  width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n  display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n  display: none !important;\n}\n@media (max-width: 767px) {\n  .visible-xs {\n    display: block !important;\n  }\n  table.visible-xs {\n    display: table !important;\n  }\n  tr.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-xs,\n  td.visible-xs {\n    display: table-cell !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-block {\n    display: block !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-inline {\n    display: inline !important;\n  }\n}\n@media (max-width: 767px) {\n  .visible-xs-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm {\n    display: block !important;\n  }\n  table.visible-sm {\n    display: table !important;\n  }\n  tr.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-sm,\n  td.visible-sm {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-block {\n    display: block !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md {\n    display: block !important;\n  }\n  table.visible-md {\n    display: table !important;\n  }\n  tr.visible-md {\n    display: table-row !important;\n  }\n  th.visible-md,\n  td.visible-md {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-block {\n    display: block !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg {\n    display: block !important;\n  }\n  table.visible-lg {\n    display: table !important;\n  }\n  tr.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-lg,\n  td.visible-lg {\n    display: table-cell !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-block {\n    display: block !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-inline {\n    display: inline !important;\n  }\n}\n@media (min-width: 1200px) {\n  .visible-lg-inline-block {\n    display: inline-block !important;\n  }\n}\n@media (max-width: 767px) {\n  .hidden-xs {\n    display: none !important;\n  }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-sm {\n    display: none !important;\n  }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-md {\n    display: none !important;\n  }\n}\n@media (min-width: 1200px) {\n  .hidden-lg {\n    display: none !important;\n  }\n}\n.visible-print {\n  display: none !important;\n}\n@media print {\n  .visible-print {\n    display: block !important;\n  }\n  table.visible-print {\n    display: table !important;\n  }\n  tr.visible-print {\n    display: table-row !important;\n  }\n  th.visible-print,\n  td.visible-print {\n    display: table-cell !important;\n  }\n}\n.visible-print-block {\n  display: none !important;\n}\n@media print {\n  .visible-print-block {\n    display: block !important;\n  }\n}\n.visible-print-inline {\n  display: none !important;\n}\n@media print {\n  .visible-print-inline {\n    display: inline !important;\n  }\n}\n.visible-print-inline-block {\n  display: none !important;\n}\n@media print {\n  .visible-print-inline-block {\n    display: inline-block !important;\n  }\n}\n@media print {\n  .hidden-print {\n    display: none !important;\n  }\n}\n/*# sourceMappingURL=bootstrap.css.map */\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/bootstrap/dist/js/bootstrap.js",
    "content": "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under the MIT license\n */\n\nif (typeof jQuery === 'undefined') {\n  throw new Error('Bootstrap\\'s JavaScript requires jQuery')\n}\n\n+function ($) {\n  'use strict';\n  var version = $.fn.jquery.split(' ')[0].split('.')\n  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) {\n    throw new Error('Bootstrap\\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4')\n  }\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: transition.js v3.3.7\n * http://getbootstrap.com/javascript/#transitions\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)\n  // ============================================================\n\n  function transitionEnd() {\n    var el = document.createElement('bootstrap')\n\n    var transEndEventNames = {\n      WebkitTransition : 'webkitTransitionEnd',\n      MozTransition    : 'transitionend',\n      OTransition      : 'oTransitionEnd otransitionend',\n      transition       : 'transitionend'\n    }\n\n    for (var name in transEndEventNames) {\n      if (el.style[name] !== undefined) {\n        return { end: transEndEventNames[name] }\n      }\n    }\n\n    return false // explicit for ie8 (  ._.)\n  }\n\n  // http://blog.alexmaccaw.com/css-transitions\n  $.fn.emulateTransitionEnd = function (duration) {\n    var called = false\n    var $el = this\n    $(this).one('bsTransitionEnd', function () { called = true })\n    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }\n    setTimeout(callback, duration)\n    return this\n  }\n\n  $(function () {\n    $.support.transition = transitionEnd()\n\n    if (!$.support.transition) return\n\n    $.event.special.bsTransitionEnd = {\n      bindType: $.support.transition.end,\n      delegateType: $.support.transition.end,\n      handle: function (e) {\n        if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)\n      }\n    }\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: alert.js v3.3.7\n * http://getbootstrap.com/javascript/#alerts\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // ALERT CLASS DEFINITION\n  // ======================\n\n  var dismiss = '[data-dismiss=\"alert\"]'\n  var Alert   = function (el) {\n    $(el).on('click', dismiss, this.close)\n  }\n\n  Alert.VERSION = '3.3.7'\n\n  Alert.TRANSITION_DURATION = 150\n\n  Alert.prototype.close = function (e) {\n    var $this    = $(this)\n    var selector = $this.attr('data-target')\n\n    if (!selector) {\n      selector = $this.attr('href')\n      selector = selector && selector.replace(/.*(?=#[^\\s]*$)/, '') // strip for ie7\n    }\n\n    var $parent = $(selector === '#' ? [] : selector)\n\n    if (e) e.preventDefault()\n\n    if (!$parent.length) {\n      $parent = $this.closest('.alert')\n    }\n\n    $parent.trigger(e = $.Event('close.bs.alert'))\n\n    if (e.isDefaultPrevented()) return\n\n    $parent.removeClass('in')\n\n    function removeElement() {\n      // detach from parent, fire event then clean up data\n      $parent.detach().trigger('closed.bs.alert').remove()\n    }\n\n    $.support.transition && $parent.hasClass('fade') ?\n      $parent\n        .one('bsTransitionEnd', removeElement)\n        .emulateTransitionEnd(Alert.TRANSITION_DURATION) :\n      removeElement()\n  }\n\n\n  // ALERT PLUGIN DEFINITION\n  // =======================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this = $(this)\n      var data  = $this.data('bs.alert')\n\n      if (!data) $this.data('bs.alert', (data = new Alert(this)))\n      if (typeof option == 'string') data[option].call($this)\n    })\n  }\n\n  var old = $.fn.alert\n\n  $.fn.alert             = Plugin\n  $.fn.alert.Constructor = Alert\n\n\n  // ALERT NO CONFLICT\n  // =================\n\n  $.fn.alert.noConflict = function () {\n    $.fn.alert = old\n    return this\n  }\n\n\n  // ALERT DATA-API\n  // ==============\n\n  $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: button.js v3.3.7\n * http://getbootstrap.com/javascript/#buttons\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // BUTTON PUBLIC CLASS DEFINITION\n  // ==============================\n\n  var Button = function (element, options) {\n    this.$element  = $(element)\n    this.options   = $.extend({}, Button.DEFAULTS, options)\n    this.isLoading = false\n  }\n\n  Button.VERSION  = '3.3.7'\n\n  Button.DEFAULTS = {\n    loadingText: 'loading...'\n  }\n\n  Button.prototype.setState = function (state) {\n    var d    = 'disabled'\n    var $el  = this.$element\n    var val  = $el.is('input') ? 'val' : 'html'\n    var data = $el.data()\n\n    state += 'Text'\n\n    if (data.resetText == null) $el.data('resetText', $el[val]())\n\n    // push to event loop to allow forms to submit\n    setTimeout($.proxy(function () {\n      $el[val](data[state] == null ? this.options[state] : data[state])\n\n      if (state == 'loadingText') {\n        this.isLoading = true\n        $el.addClass(d).attr(d, d).prop(d, true)\n      } else if (this.isLoading) {\n        this.isLoading = false\n        $el.removeClass(d).removeAttr(d).prop(d, false)\n      }\n    }, this), 0)\n  }\n\n  Button.prototype.toggle = function () {\n    var changed = true\n    var $parent = this.$element.closest('[data-toggle=\"buttons\"]')\n\n    if ($parent.length) {\n      var $input = this.$element.find('input')\n      if ($input.prop('type') == 'radio') {\n        if ($input.prop('checked')) changed = false\n        $parent.find('.active').removeClass('active')\n        this.$element.addClass('active')\n      } else if ($input.prop('type') == 'checkbox') {\n        if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false\n        this.$element.toggleClass('active')\n      }\n      $input.prop('checked', this.$element.hasClass('active'))\n      if (changed) $input.trigger('change')\n    } else {\n      this.$element.attr('aria-pressed', !this.$element.hasClass('active'))\n      this.$element.toggleClass('active')\n    }\n  }\n\n\n  // BUTTON PLUGIN DEFINITION\n  // ========================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.button')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.button', (data = new Button(this, options)))\n\n      if (option == 'toggle') data.toggle()\n      else if (option) data.setState(option)\n    })\n  }\n\n  var old = $.fn.button\n\n  $.fn.button             = Plugin\n  $.fn.button.Constructor = Button\n\n\n  // BUTTON NO CONFLICT\n  // ==================\n\n  $.fn.button.noConflict = function () {\n    $.fn.button = old\n    return this\n  }\n\n\n  // BUTTON DATA-API\n  // ===============\n\n  $(document)\n    .on('click.bs.button.data-api', '[data-toggle^=\"button\"]', function (e) {\n      var $btn = $(e.target).closest('.btn')\n      Plugin.call($btn, 'toggle')\n      if (!($(e.target).is('input[type=\"radio\"], input[type=\"checkbox\"]'))) {\n        // Prevent double click on radios, and the double selections (so cancellation) on checkboxes\n        e.preventDefault()\n        // The target component still receive the focus\n        if ($btn.is('input,button')) $btn.trigger('focus')\n        else $btn.find('input:visible,button:visible').first().trigger('focus')\n      }\n    })\n    .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^=\"button\"]', function (e) {\n      $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))\n    })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: carousel.js v3.3.7\n * http://getbootstrap.com/javascript/#carousel\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // CAROUSEL CLASS DEFINITION\n  // =========================\n\n  var Carousel = function (element, options) {\n    this.$element    = $(element)\n    this.$indicators = this.$element.find('.carousel-indicators')\n    this.options     = options\n    this.paused      = null\n    this.sliding     = null\n    this.interval    = null\n    this.$active     = null\n    this.$items      = null\n\n    this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))\n\n    this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element\n      .on('mouseenter.bs.carousel', $.proxy(this.pause, this))\n      .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))\n  }\n\n  Carousel.VERSION  = '3.3.7'\n\n  Carousel.TRANSITION_DURATION = 600\n\n  Carousel.DEFAULTS = {\n    interval: 5000,\n    pause: 'hover',\n    wrap: true,\n    keyboard: true\n  }\n\n  Carousel.prototype.keydown = function (e) {\n    if (/input|textarea/i.test(e.target.tagName)) return\n    switch (e.which) {\n      case 37: this.prev(); break\n      case 39: this.next(); break\n      default: return\n    }\n\n    e.preventDefault()\n  }\n\n  Carousel.prototype.cycle = function (e) {\n    e || (this.paused = false)\n\n    this.interval && clearInterval(this.interval)\n\n    this.options.interval\n      && !this.paused\n      && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))\n\n    return this\n  }\n\n  Carousel.prototype.getItemIndex = function (item) {\n    this.$items = item.parent().children('.item')\n    return this.$items.index(item || this.$active)\n  }\n\n  Carousel.prototype.getItemForDirection = function (direction, active) {\n    var activeIndex = this.getItemIndex(active)\n    var willWrap = (direction == 'prev' && activeIndex === 0)\n                || (direction == 'next' && activeIndex == (this.$items.length - 1))\n    if (willWrap && !this.options.wrap) return active\n    var delta = direction == 'prev' ? -1 : 1\n    var itemIndex = (activeIndex + delta) % this.$items.length\n    return this.$items.eq(itemIndex)\n  }\n\n  Carousel.prototype.to = function (pos) {\n    var that        = this\n    var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))\n\n    if (pos > (this.$items.length - 1) || pos < 0) return\n\n    if (this.sliding)       return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, \"slid\"\n    if (activeIndex == pos) return this.pause().cycle()\n\n    return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))\n  }\n\n  Carousel.prototype.pause = function (e) {\n    e || (this.paused = true)\n\n    if (this.$element.find('.next, .prev').length && $.support.transition) {\n      this.$element.trigger($.support.transition.end)\n      this.cycle(true)\n    }\n\n    this.interval = clearInterval(this.interval)\n\n    return this\n  }\n\n  Carousel.prototype.next = function () {\n    if (this.sliding) return\n    return this.slide('next')\n  }\n\n  Carousel.prototype.prev = function () {\n    if (this.sliding) return\n    return this.slide('prev')\n  }\n\n  Carousel.prototype.slide = function (type, next) {\n    var $active   = this.$element.find('.item.active')\n    var $next     = next || this.getItemForDirection(type, $active)\n    var isCycling = this.interval\n    var direction = type == 'next' ? 'left' : 'right'\n    var that      = this\n\n    if ($next.hasClass('active')) return (this.sliding = false)\n\n    var relatedTarget = $next[0]\n    var slideEvent = $.Event('slide.bs.carousel', {\n      relatedTarget: relatedTarget,\n      direction: direction\n    })\n    this.$element.trigger(slideEvent)\n    if (slideEvent.isDefaultPrevented()) return\n\n    this.sliding = true\n\n    isCycling && this.pause()\n\n    if (this.$indicators.length) {\n      this.$indicators.find('.active').removeClass('active')\n      var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])\n      $nextIndicator && $nextIndicator.addClass('active')\n    }\n\n    var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, \"slid\"\n    if ($.support.transition && this.$element.hasClass('slide')) {\n      $next.addClass(type)\n      $next[0].offsetWidth // force reflow\n      $active.addClass(direction)\n      $next.addClass(direction)\n      $active\n        .one('bsTransitionEnd', function () {\n          $next.removeClass([type, direction].join(' ')).addClass('active')\n          $active.removeClass(['active', direction].join(' '))\n          that.sliding = false\n          setTimeout(function () {\n            that.$element.trigger(slidEvent)\n          }, 0)\n        })\n        .emulateTransitionEnd(Carousel.TRANSITION_DURATION)\n    } else {\n      $active.removeClass('active')\n      $next.addClass('active')\n      this.sliding = false\n      this.$element.trigger(slidEvent)\n    }\n\n    isCycling && this.cycle()\n\n    return this\n  }\n\n\n  // CAROUSEL PLUGIN DEFINITION\n  // ==========================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.carousel')\n      var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)\n      var action  = typeof option == 'string' ? option : options.slide\n\n      if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))\n      if (typeof option == 'number') data.to(option)\n      else if (action) data[action]()\n      else if (options.interval) data.pause().cycle()\n    })\n  }\n\n  var old = $.fn.carousel\n\n  $.fn.carousel             = Plugin\n  $.fn.carousel.Constructor = Carousel\n\n\n  // CAROUSEL NO CONFLICT\n  // ====================\n\n  $.fn.carousel.noConflict = function () {\n    $.fn.carousel = old\n    return this\n  }\n\n\n  // CAROUSEL DATA-API\n  // =================\n\n  var clickHandler = function (e) {\n    var href\n    var $this   = $(this)\n    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\\s]+$)/, '')) // strip for ie7\n    if (!$target.hasClass('carousel')) return\n    var options = $.extend({}, $target.data(), $this.data())\n    var slideIndex = $this.attr('data-slide-to')\n    if (slideIndex) options.interval = false\n\n    Plugin.call($target, options)\n\n    if (slideIndex) {\n      $target.data('bs.carousel').to(slideIndex)\n    }\n\n    e.preventDefault()\n  }\n\n  $(document)\n    .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)\n    .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)\n\n  $(window).on('load', function () {\n    $('[data-ride=\"carousel\"]').each(function () {\n      var $carousel = $(this)\n      Plugin.call($carousel, $carousel.data())\n    })\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: collapse.js v3.3.7\n * http://getbootstrap.com/javascript/#collapse\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n/* jshint latedef: false */\n\n+function ($) {\n  'use strict';\n\n  // COLLAPSE PUBLIC CLASS DEFINITION\n  // ================================\n\n  var Collapse = function (element, options) {\n    this.$element      = $(element)\n    this.options       = $.extend({}, Collapse.DEFAULTS, options)\n    this.$trigger      = $('[data-toggle=\"collapse\"][href=\"#' + element.id + '\"],' +\n                           '[data-toggle=\"collapse\"][data-target=\"#' + element.id + '\"]')\n    this.transitioning = null\n\n    if (this.options.parent) {\n      this.$parent = this.getParent()\n    } else {\n      this.addAriaAndCollapsedClass(this.$element, this.$trigger)\n    }\n\n    if (this.options.toggle) this.toggle()\n  }\n\n  Collapse.VERSION  = '3.3.7'\n\n  Collapse.TRANSITION_DURATION = 350\n\n  Collapse.DEFAULTS = {\n    toggle: true\n  }\n\n  Collapse.prototype.dimension = function () {\n    var hasWidth = this.$element.hasClass('width')\n    return hasWidth ? 'width' : 'height'\n  }\n\n  Collapse.prototype.show = function () {\n    if (this.transitioning || this.$element.hasClass('in')) return\n\n    var activesData\n    var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')\n\n    if (actives && actives.length) {\n      activesData = actives.data('bs.collapse')\n      if (activesData && activesData.transitioning) return\n    }\n\n    var startEvent = $.Event('show.bs.collapse')\n    this.$element.trigger(startEvent)\n    if (startEvent.isDefaultPrevented()) return\n\n    if (actives && actives.length) {\n      Plugin.call(actives, 'hide')\n      activesData || actives.data('bs.collapse', null)\n    }\n\n    var dimension = this.dimension()\n\n    this.$element\n      .removeClass('collapse')\n      .addClass('collapsing')[dimension](0)\n      .attr('aria-expanded', true)\n\n    this.$trigger\n      .removeClass('collapsed')\n      .attr('aria-expanded', true)\n\n    this.transitioning = 1\n\n    var complete = function () {\n      this.$element\n        .removeClass('collapsing')\n        .addClass('collapse in')[dimension]('')\n      this.transitioning = 0\n      this.$element\n        .trigger('shown.bs.collapse')\n    }\n\n    if (!$.support.transition) return complete.call(this)\n\n    var scrollSize = $.camelCase(['scroll', dimension].join('-'))\n\n    this.$element\n      .one('bsTransitionEnd', $.proxy(complete, this))\n      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])\n  }\n\n  Collapse.prototype.hide = function () {\n    if (this.transitioning || !this.$element.hasClass('in')) return\n\n    var startEvent = $.Event('hide.bs.collapse')\n    this.$element.trigger(startEvent)\n    if (startEvent.isDefaultPrevented()) return\n\n    var dimension = this.dimension()\n\n    this.$element[dimension](this.$element[dimension]())[0].offsetHeight\n\n    this.$element\n      .addClass('collapsing')\n      .removeClass('collapse in')\n      .attr('aria-expanded', false)\n\n    this.$trigger\n      .addClass('collapsed')\n      .attr('aria-expanded', false)\n\n    this.transitioning = 1\n\n    var complete = function () {\n      this.transitioning = 0\n      this.$element\n        .removeClass('collapsing')\n        .addClass('collapse')\n        .trigger('hidden.bs.collapse')\n    }\n\n    if (!$.support.transition) return complete.call(this)\n\n    this.$element\n      [dimension](0)\n      .one('bsTransitionEnd', $.proxy(complete, this))\n      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)\n  }\n\n  Collapse.prototype.toggle = function () {\n    this[this.$element.hasClass('in') ? 'hide' : 'show']()\n  }\n\n  Collapse.prototype.getParent = function () {\n    return $(this.options.parent)\n      .find('[data-toggle=\"collapse\"][data-parent=\"' + this.options.parent + '\"]')\n      .each($.proxy(function (i, element) {\n        var $element = $(element)\n        this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)\n      }, this))\n      .end()\n  }\n\n  Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {\n    var isOpen = $element.hasClass('in')\n\n    $element.attr('aria-expanded', isOpen)\n    $trigger\n      .toggleClass('collapsed', !isOpen)\n      .attr('aria-expanded', isOpen)\n  }\n\n  function getTargetFromTrigger($trigger) {\n    var href\n    var target = $trigger.attr('data-target')\n      || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\\s]+$)/, '') // strip for ie7\n\n    return $(target)\n  }\n\n\n  // COLLAPSE PLUGIN DEFINITION\n  // ==========================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.collapse')\n      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)\n\n      if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false\n      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  var old = $.fn.collapse\n\n  $.fn.collapse             = Plugin\n  $.fn.collapse.Constructor = Collapse\n\n\n  // COLLAPSE NO CONFLICT\n  // ====================\n\n  $.fn.collapse.noConflict = function () {\n    $.fn.collapse = old\n    return this\n  }\n\n\n  // COLLAPSE DATA-API\n  // =================\n\n  $(document).on('click.bs.collapse.data-api', '[data-toggle=\"collapse\"]', function (e) {\n    var $this   = $(this)\n\n    if (!$this.attr('data-target')) e.preventDefault()\n\n    var $target = getTargetFromTrigger($this)\n    var data    = $target.data('bs.collapse')\n    var option  = data ? 'toggle' : $this.data()\n\n    Plugin.call($target, option)\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: dropdown.js v3.3.7\n * http://getbootstrap.com/javascript/#dropdowns\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // DROPDOWN CLASS DEFINITION\n  // =========================\n\n  var backdrop = '.dropdown-backdrop'\n  var toggle   = '[data-toggle=\"dropdown\"]'\n  var Dropdown = function (element) {\n    $(element).on('click.bs.dropdown', this.toggle)\n  }\n\n  Dropdown.VERSION = '3.3.7'\n\n  function getParent($this) {\n    var selector = $this.attr('data-target')\n\n    if (!selector) {\n      selector = $this.attr('href')\n      selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\\s]*$)/, '') // strip for ie7\n    }\n\n    var $parent = selector && $(selector)\n\n    return $parent && $parent.length ? $parent : $this.parent()\n  }\n\n  function clearMenus(e) {\n    if (e && e.which === 3) return\n    $(backdrop).remove()\n    $(toggle).each(function () {\n      var $this         = $(this)\n      var $parent       = getParent($this)\n      var relatedTarget = { relatedTarget: this }\n\n      if (!$parent.hasClass('open')) return\n\n      if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return\n\n      $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))\n\n      if (e.isDefaultPrevented()) return\n\n      $this.attr('aria-expanded', 'false')\n      $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))\n    })\n  }\n\n  Dropdown.prototype.toggle = function (e) {\n    var $this = $(this)\n\n    if ($this.is('.disabled, :disabled')) return\n\n    var $parent  = getParent($this)\n    var isActive = $parent.hasClass('open')\n\n    clearMenus()\n\n    if (!isActive) {\n      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {\n        // if mobile we use a backdrop because click events don't delegate\n        $(document.createElement('div'))\n          .addClass('dropdown-backdrop')\n          .insertAfter($(this))\n          .on('click', clearMenus)\n      }\n\n      var relatedTarget = { relatedTarget: this }\n      $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))\n\n      if (e.isDefaultPrevented()) return\n\n      $this\n        .trigger('focus')\n        .attr('aria-expanded', 'true')\n\n      $parent\n        .toggleClass('open')\n        .trigger($.Event('shown.bs.dropdown', relatedTarget))\n    }\n\n    return false\n  }\n\n  Dropdown.prototype.keydown = function (e) {\n    if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return\n\n    var $this = $(this)\n\n    e.preventDefault()\n    e.stopPropagation()\n\n    if ($this.is('.disabled, :disabled')) return\n\n    var $parent  = getParent($this)\n    var isActive = $parent.hasClass('open')\n\n    if (!isActive && e.which != 27 || isActive && e.which == 27) {\n      if (e.which == 27) $parent.find(toggle).trigger('focus')\n      return $this.trigger('click')\n    }\n\n    var desc = ' li:not(.disabled):visible a'\n    var $items = $parent.find('.dropdown-menu' + desc)\n\n    if (!$items.length) return\n\n    var index = $items.index(e.target)\n\n    if (e.which == 38 && index > 0)                 index--         // up\n    if (e.which == 40 && index < $items.length - 1) index++         // down\n    if (!~index)                                    index = 0\n\n    $items.eq(index).trigger('focus')\n  }\n\n\n  // DROPDOWN PLUGIN DEFINITION\n  // ==========================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this = $(this)\n      var data  = $this.data('bs.dropdown')\n\n      if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))\n      if (typeof option == 'string') data[option].call($this)\n    })\n  }\n\n  var old = $.fn.dropdown\n\n  $.fn.dropdown             = Plugin\n  $.fn.dropdown.Constructor = Dropdown\n\n\n  // DROPDOWN NO CONFLICT\n  // ====================\n\n  $.fn.dropdown.noConflict = function () {\n    $.fn.dropdown = old\n    return this\n  }\n\n\n  // APPLY TO STANDARD DROPDOWN ELEMENTS\n  // ===================================\n\n  $(document)\n    .on('click.bs.dropdown.data-api', clearMenus)\n    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })\n    .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)\n    .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)\n    .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: modal.js v3.3.7\n * http://getbootstrap.com/javascript/#modals\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // MODAL CLASS DEFINITION\n  // ======================\n\n  var Modal = function (element, options) {\n    this.options             = options\n    this.$body               = $(document.body)\n    this.$element            = $(element)\n    this.$dialog             = this.$element.find('.modal-dialog')\n    this.$backdrop           = null\n    this.isShown             = null\n    this.originalBodyPad     = null\n    this.scrollbarWidth      = 0\n    this.ignoreBackdropClick = false\n\n    if (this.options.remote) {\n      this.$element\n        .find('.modal-content')\n        .load(this.options.remote, $.proxy(function () {\n          this.$element.trigger('loaded.bs.modal')\n        }, this))\n    }\n  }\n\n  Modal.VERSION  = '3.3.7'\n\n  Modal.TRANSITION_DURATION = 300\n  Modal.BACKDROP_TRANSITION_DURATION = 150\n\n  Modal.DEFAULTS = {\n    backdrop: true,\n    keyboard: true,\n    show: true\n  }\n\n  Modal.prototype.toggle = function (_relatedTarget) {\n    return this.isShown ? this.hide() : this.show(_relatedTarget)\n  }\n\n  Modal.prototype.show = function (_relatedTarget) {\n    var that = this\n    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })\n\n    this.$element.trigger(e)\n\n    if (this.isShown || e.isDefaultPrevented()) return\n\n    this.isShown = true\n\n    this.checkScrollbar()\n    this.setScrollbar()\n    this.$body.addClass('modal-open')\n\n    this.escape()\n    this.resize()\n\n    this.$element.on('click.dismiss.bs.modal', '[data-dismiss=\"modal\"]', $.proxy(this.hide, this))\n\n    this.$dialog.on('mousedown.dismiss.bs.modal', function () {\n      that.$element.one('mouseup.dismiss.bs.modal', function (e) {\n        if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true\n      })\n    })\n\n    this.backdrop(function () {\n      var transition = $.support.transition && that.$element.hasClass('fade')\n\n      if (!that.$element.parent().length) {\n        that.$element.appendTo(that.$body) // don't move modals dom position\n      }\n\n      that.$element\n        .show()\n        .scrollTop(0)\n\n      that.adjustDialog()\n\n      if (transition) {\n        that.$element[0].offsetWidth // force reflow\n      }\n\n      that.$element.addClass('in')\n\n      that.enforceFocus()\n\n      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })\n\n      transition ?\n        that.$dialog // wait for modal to slide in\n          .one('bsTransitionEnd', function () {\n            that.$element.trigger('focus').trigger(e)\n          })\n          .emulateTransitionEnd(Modal.TRANSITION_DURATION) :\n        that.$element.trigger('focus').trigger(e)\n    })\n  }\n\n  Modal.prototype.hide = function (e) {\n    if (e) e.preventDefault()\n\n    e = $.Event('hide.bs.modal')\n\n    this.$element.trigger(e)\n\n    if (!this.isShown || e.isDefaultPrevented()) return\n\n    this.isShown = false\n\n    this.escape()\n    this.resize()\n\n    $(document).off('focusin.bs.modal')\n\n    this.$element\n      .removeClass('in')\n      .off('click.dismiss.bs.modal')\n      .off('mouseup.dismiss.bs.modal')\n\n    this.$dialog.off('mousedown.dismiss.bs.modal')\n\n    $.support.transition && this.$element.hasClass('fade') ?\n      this.$element\n        .one('bsTransitionEnd', $.proxy(this.hideModal, this))\n        .emulateTransitionEnd(Modal.TRANSITION_DURATION) :\n      this.hideModal()\n  }\n\n  Modal.prototype.enforceFocus = function () {\n    $(document)\n      .off('focusin.bs.modal') // guard against infinite focus loop\n      .on('focusin.bs.modal', $.proxy(function (e) {\n        if (document !== e.target &&\n            this.$element[0] !== e.target &&\n            !this.$element.has(e.target).length) {\n          this.$element.trigger('focus')\n        }\n      }, this))\n  }\n\n  Modal.prototype.escape = function () {\n    if (this.isShown && this.options.keyboard) {\n      this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {\n        e.which == 27 && this.hide()\n      }, this))\n    } else if (!this.isShown) {\n      this.$element.off('keydown.dismiss.bs.modal')\n    }\n  }\n\n  Modal.prototype.resize = function () {\n    if (this.isShown) {\n      $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))\n    } else {\n      $(window).off('resize.bs.modal')\n    }\n  }\n\n  Modal.prototype.hideModal = function () {\n    var that = this\n    this.$element.hide()\n    this.backdrop(function () {\n      that.$body.removeClass('modal-open')\n      that.resetAdjustments()\n      that.resetScrollbar()\n      that.$element.trigger('hidden.bs.modal')\n    })\n  }\n\n  Modal.prototype.removeBackdrop = function () {\n    this.$backdrop && this.$backdrop.remove()\n    this.$backdrop = null\n  }\n\n  Modal.prototype.backdrop = function (callback) {\n    var that = this\n    var animate = this.$element.hasClass('fade') ? 'fade' : ''\n\n    if (this.isShown && this.options.backdrop) {\n      var doAnimate = $.support.transition && animate\n\n      this.$backdrop = $(document.createElement('div'))\n        .addClass('modal-backdrop ' + animate)\n        .appendTo(this.$body)\n\n      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {\n        if (this.ignoreBackdropClick) {\n          this.ignoreBackdropClick = false\n          return\n        }\n        if (e.target !== e.currentTarget) return\n        this.options.backdrop == 'static'\n          ? this.$element[0].focus()\n          : this.hide()\n      }, this))\n\n      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow\n\n      this.$backdrop.addClass('in')\n\n      if (!callback) return\n\n      doAnimate ?\n        this.$backdrop\n          .one('bsTransitionEnd', callback)\n          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :\n        callback()\n\n    } else if (!this.isShown && this.$backdrop) {\n      this.$backdrop.removeClass('in')\n\n      var callbackRemove = function () {\n        that.removeBackdrop()\n        callback && callback()\n      }\n      $.support.transition && this.$element.hasClass('fade') ?\n        this.$backdrop\n          .one('bsTransitionEnd', callbackRemove)\n          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :\n        callbackRemove()\n\n    } else if (callback) {\n      callback()\n    }\n  }\n\n  // these following methods are used to handle overflowing modals\n\n  Modal.prototype.handleUpdate = function () {\n    this.adjustDialog()\n  }\n\n  Modal.prototype.adjustDialog = function () {\n    var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight\n\n    this.$element.css({\n      paddingLeft:  !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',\n      paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''\n    })\n  }\n\n  Modal.prototype.resetAdjustments = function () {\n    this.$element.css({\n      paddingLeft: '',\n      paddingRight: ''\n    })\n  }\n\n  Modal.prototype.checkScrollbar = function () {\n    var fullWindowWidth = window.innerWidth\n    if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8\n      var documentElementRect = document.documentElement.getBoundingClientRect()\n      fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)\n    }\n    this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth\n    this.scrollbarWidth = this.measureScrollbar()\n  }\n\n  Modal.prototype.setScrollbar = function () {\n    var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)\n    this.originalBodyPad = document.body.style.paddingRight || ''\n    if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)\n  }\n\n  Modal.prototype.resetScrollbar = function () {\n    this.$body.css('padding-right', this.originalBodyPad)\n  }\n\n  Modal.prototype.measureScrollbar = function () { // thx walsh\n    var scrollDiv = document.createElement('div')\n    scrollDiv.className = 'modal-scrollbar-measure'\n    this.$body.append(scrollDiv)\n    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth\n    this.$body[0].removeChild(scrollDiv)\n    return scrollbarWidth\n  }\n\n\n  // MODAL PLUGIN DEFINITION\n  // =======================\n\n  function Plugin(option, _relatedTarget) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.modal')\n      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)\n\n      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))\n      if (typeof option == 'string') data[option](_relatedTarget)\n      else if (options.show) data.show(_relatedTarget)\n    })\n  }\n\n  var old = $.fn.modal\n\n  $.fn.modal             = Plugin\n  $.fn.modal.Constructor = Modal\n\n\n  // MODAL NO CONFLICT\n  // =================\n\n  $.fn.modal.noConflict = function () {\n    $.fn.modal = old\n    return this\n  }\n\n\n  // MODAL DATA-API\n  // ==============\n\n  $(document).on('click.bs.modal.data-api', '[data-toggle=\"modal\"]', function (e) {\n    var $this   = $(this)\n    var href    = $this.attr('href')\n    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\\s]+$)/, ''))) // strip for ie7\n    var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())\n\n    if ($this.is('a')) e.preventDefault()\n\n    $target.one('show.bs.modal', function (showEvent) {\n      if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown\n      $target.one('hidden.bs.modal', function () {\n        $this.is(':visible') && $this.trigger('focus')\n      })\n    })\n    Plugin.call($target, option, this)\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: tooltip.js v3.3.7\n * http://getbootstrap.com/javascript/#tooltip\n * Inspired by the original jQuery.tipsy by Jason Frame\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // TOOLTIP PUBLIC CLASS DEFINITION\n  // ===============================\n\n  var Tooltip = function (element, options) {\n    this.type       = null\n    this.options    = null\n    this.enabled    = null\n    this.timeout    = null\n    this.hoverState = null\n    this.$element   = null\n    this.inState    = null\n\n    this.init('tooltip', element, options)\n  }\n\n  Tooltip.VERSION  = '3.3.7'\n\n  Tooltip.TRANSITION_DURATION = 150\n\n  Tooltip.DEFAULTS = {\n    animation: true,\n    placement: 'top',\n    selector: false,\n    template: '<div class=\"tooltip\" role=\"tooltip\"><div class=\"tooltip-arrow\"></div><div class=\"tooltip-inner\"></div></div>',\n    trigger: 'hover focus',\n    title: '',\n    delay: 0,\n    html: false,\n    container: false,\n    viewport: {\n      selector: 'body',\n      padding: 0\n    }\n  }\n\n  Tooltip.prototype.init = function (type, element, options) {\n    this.enabled   = true\n    this.type      = type\n    this.$element  = $(element)\n    this.options   = this.getOptions(options)\n    this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))\n    this.inState   = { click: false, hover: false, focus: false }\n\n    if (this.$element[0] instanceof document.constructor && !this.options.selector) {\n      throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')\n    }\n\n    var triggers = this.options.trigger.split(' ')\n\n    for (var i = triggers.length; i--;) {\n      var trigger = triggers[i]\n\n      if (trigger == 'click') {\n        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))\n      } else if (trigger != 'manual') {\n        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'\n        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'\n\n        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))\n        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))\n      }\n    }\n\n    this.options.selector ?\n      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :\n      this.fixTitle()\n  }\n\n  Tooltip.prototype.getDefaults = function () {\n    return Tooltip.DEFAULTS\n  }\n\n  Tooltip.prototype.getOptions = function (options) {\n    options = $.extend({}, this.getDefaults(), this.$element.data(), options)\n\n    if (options.delay && typeof options.delay == 'number') {\n      options.delay = {\n        show: options.delay,\n        hide: options.delay\n      }\n    }\n\n    return options\n  }\n\n  Tooltip.prototype.getDelegateOptions = function () {\n    var options  = {}\n    var defaults = this.getDefaults()\n\n    this._options && $.each(this._options, function (key, value) {\n      if (defaults[key] != value) options[key] = value\n    })\n\n    return options\n  }\n\n  Tooltip.prototype.enter = function (obj) {\n    var self = obj instanceof this.constructor ?\n      obj : $(obj.currentTarget).data('bs.' + this.type)\n\n    if (!self) {\n      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())\n      $(obj.currentTarget).data('bs.' + this.type, self)\n    }\n\n    if (obj instanceof $.Event) {\n      self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true\n    }\n\n    if (self.tip().hasClass('in') || self.hoverState == 'in') {\n      self.hoverState = 'in'\n      return\n    }\n\n    clearTimeout(self.timeout)\n\n    self.hoverState = 'in'\n\n    if (!self.options.delay || !self.options.delay.show) return self.show()\n\n    self.timeout = setTimeout(function () {\n      if (self.hoverState == 'in') self.show()\n    }, self.options.delay.show)\n  }\n\n  Tooltip.prototype.isInStateTrue = function () {\n    for (var key in this.inState) {\n      if (this.inState[key]) return true\n    }\n\n    return false\n  }\n\n  Tooltip.prototype.leave = function (obj) {\n    var self = obj instanceof this.constructor ?\n      obj : $(obj.currentTarget).data('bs.' + this.type)\n\n    if (!self) {\n      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())\n      $(obj.currentTarget).data('bs.' + this.type, self)\n    }\n\n    if (obj instanceof $.Event) {\n      self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false\n    }\n\n    if (self.isInStateTrue()) return\n\n    clearTimeout(self.timeout)\n\n    self.hoverState = 'out'\n\n    if (!self.options.delay || !self.options.delay.hide) return self.hide()\n\n    self.timeout = setTimeout(function () {\n      if (self.hoverState == 'out') self.hide()\n    }, self.options.delay.hide)\n  }\n\n  Tooltip.prototype.show = function () {\n    var e = $.Event('show.bs.' + this.type)\n\n    if (this.hasContent() && this.enabled) {\n      this.$element.trigger(e)\n\n      var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])\n      if (e.isDefaultPrevented() || !inDom) return\n      var that = this\n\n      var $tip = this.tip()\n\n      var tipId = this.getUID(this.type)\n\n      this.setContent()\n      $tip.attr('id', tipId)\n      this.$element.attr('aria-describedby', tipId)\n\n      if (this.options.animation) $tip.addClass('fade')\n\n      var placement = typeof this.options.placement == 'function' ?\n        this.options.placement.call(this, $tip[0], this.$element[0]) :\n        this.options.placement\n\n      var autoToken = /\\s?auto?\\s?/i\n      var autoPlace = autoToken.test(placement)\n      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'\n\n      $tip\n        .detach()\n        .css({ top: 0, left: 0, display: 'block' })\n        .addClass(placement)\n        .data('bs.' + this.type, this)\n\n      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)\n      this.$element.trigger('inserted.bs.' + this.type)\n\n      var pos          = this.getPosition()\n      var actualWidth  = $tip[0].offsetWidth\n      var actualHeight = $tip[0].offsetHeight\n\n      if (autoPlace) {\n        var orgPlacement = placement\n        var viewportDim = this.getPosition(this.$viewport)\n\n        placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top'    :\n                    placement == 'top'    && pos.top    - actualHeight < viewportDim.top    ? 'bottom' :\n                    placement == 'right'  && pos.right  + actualWidth  > viewportDim.width  ? 'left'   :\n                    placement == 'left'   && pos.left   - actualWidth  < viewportDim.left   ? 'right'  :\n                    placement\n\n        $tip\n          .removeClass(orgPlacement)\n          .addClass(placement)\n      }\n\n      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)\n\n      this.applyPlacement(calculatedOffset, placement)\n\n      var complete = function () {\n        var prevHoverState = that.hoverState\n        that.$element.trigger('shown.bs.' + that.type)\n        that.hoverState = null\n\n        if (prevHoverState == 'out') that.leave(that)\n      }\n\n      $.support.transition && this.$tip.hasClass('fade') ?\n        $tip\n          .one('bsTransitionEnd', complete)\n          .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :\n        complete()\n    }\n  }\n\n  Tooltip.prototype.applyPlacement = function (offset, placement) {\n    var $tip   = this.tip()\n    var width  = $tip[0].offsetWidth\n    var height = $tip[0].offsetHeight\n\n    // manually read margins because getBoundingClientRect includes difference\n    var marginTop = parseInt($tip.css('margin-top'), 10)\n    var marginLeft = parseInt($tip.css('margin-left'), 10)\n\n    // we must check for NaN for ie 8/9\n    if (isNaN(marginTop))  marginTop  = 0\n    if (isNaN(marginLeft)) marginLeft = 0\n\n    offset.top  += marginTop\n    offset.left += marginLeft\n\n    // $.fn.offset doesn't round pixel values\n    // so we use setOffset directly with our own function B-0\n    $.offset.setOffset($tip[0], $.extend({\n      using: function (props) {\n        $tip.css({\n          top: Math.round(props.top),\n          left: Math.round(props.left)\n        })\n      }\n    }, offset), 0)\n\n    $tip.addClass('in')\n\n    // check to see if placing tip in new offset caused the tip to resize itself\n    var actualWidth  = $tip[0].offsetWidth\n    var actualHeight = $tip[0].offsetHeight\n\n    if (placement == 'top' && actualHeight != height) {\n      offset.top = offset.top + height - actualHeight\n    }\n\n    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)\n\n    if (delta.left) offset.left += delta.left\n    else offset.top += delta.top\n\n    var isVertical          = /top|bottom/.test(placement)\n    var arrowDelta          = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight\n    var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'\n\n    $tip.offset(offset)\n    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)\n  }\n\n  Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {\n    this.arrow()\n      .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')\n      .css(isVertical ? 'top' : 'left', '')\n  }\n\n  Tooltip.prototype.setContent = function () {\n    var $tip  = this.tip()\n    var title = this.getTitle()\n\n    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)\n    $tip.removeClass('fade in top bottom left right')\n  }\n\n  Tooltip.prototype.hide = function (callback) {\n    var that = this\n    var $tip = $(this.$tip)\n    var e    = $.Event('hide.bs.' + this.type)\n\n    function complete() {\n      if (that.hoverState != 'in') $tip.detach()\n      if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.\n        that.$element\n          .removeAttr('aria-describedby')\n          .trigger('hidden.bs.' + that.type)\n      }\n      callback && callback()\n    }\n\n    this.$element.trigger(e)\n\n    if (e.isDefaultPrevented()) return\n\n    $tip.removeClass('in')\n\n    $.support.transition && $tip.hasClass('fade') ?\n      $tip\n        .one('bsTransitionEnd', complete)\n        .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :\n      complete()\n\n    this.hoverState = null\n\n    return this\n  }\n\n  Tooltip.prototype.fixTitle = function () {\n    var $e = this.$element\n    if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {\n      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')\n    }\n  }\n\n  Tooltip.prototype.hasContent = function () {\n    return this.getTitle()\n  }\n\n  Tooltip.prototype.getPosition = function ($element) {\n    $element   = $element || this.$element\n\n    var el     = $element[0]\n    var isBody = el.tagName == 'BODY'\n\n    var elRect    = el.getBoundingClientRect()\n    if (elRect.width == null) {\n      // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093\n      elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })\n    }\n    var isSvg = window.SVGElement && el instanceof window.SVGElement\n    // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.\n    // See https://github.com/twbs/bootstrap/issues/20280\n    var elOffset  = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())\n    var scroll    = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }\n    var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null\n\n    return $.extend({}, elRect, scroll, outerDims, elOffset)\n  }\n\n  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {\n    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2 } :\n           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :\n           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :\n        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }\n\n  }\n\n  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {\n    var delta = { top: 0, left: 0 }\n    if (!this.$viewport) return delta\n\n    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0\n    var viewportDimensions = this.getPosition(this.$viewport)\n\n    if (/right|left/.test(placement)) {\n      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll\n      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight\n      if (topEdgeOffset < viewportDimensions.top) { // top overflow\n        delta.top = viewportDimensions.top - topEdgeOffset\n      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow\n        delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset\n      }\n    } else {\n      var leftEdgeOffset  = pos.left - viewportPadding\n      var rightEdgeOffset = pos.left + viewportPadding + actualWidth\n      if (leftEdgeOffset < viewportDimensions.left) { // left overflow\n        delta.left = viewportDimensions.left - leftEdgeOffset\n      } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow\n        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset\n      }\n    }\n\n    return delta\n  }\n\n  Tooltip.prototype.getTitle = function () {\n    var title\n    var $e = this.$element\n    var o  = this.options\n\n    title = $e.attr('data-original-title')\n      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)\n\n    return title\n  }\n\n  Tooltip.prototype.getUID = function (prefix) {\n    do prefix += ~~(Math.random() * 1000000)\n    while (document.getElementById(prefix))\n    return prefix\n  }\n\n  Tooltip.prototype.tip = function () {\n    if (!this.$tip) {\n      this.$tip = $(this.options.template)\n      if (this.$tip.length != 1) {\n        throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')\n      }\n    }\n    return this.$tip\n  }\n\n  Tooltip.prototype.arrow = function () {\n    return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))\n  }\n\n  Tooltip.prototype.enable = function () {\n    this.enabled = true\n  }\n\n  Tooltip.prototype.disable = function () {\n    this.enabled = false\n  }\n\n  Tooltip.prototype.toggleEnabled = function () {\n    this.enabled = !this.enabled\n  }\n\n  Tooltip.prototype.toggle = function (e) {\n    var self = this\n    if (e) {\n      self = $(e.currentTarget).data('bs.' + this.type)\n      if (!self) {\n        self = new this.constructor(e.currentTarget, this.getDelegateOptions())\n        $(e.currentTarget).data('bs.' + this.type, self)\n      }\n    }\n\n    if (e) {\n      self.inState.click = !self.inState.click\n      if (self.isInStateTrue()) self.enter(self)\n      else self.leave(self)\n    } else {\n      self.tip().hasClass('in') ? self.leave(self) : self.enter(self)\n    }\n  }\n\n  Tooltip.prototype.destroy = function () {\n    var that = this\n    clearTimeout(this.timeout)\n    this.hide(function () {\n      that.$element.off('.' + that.type).removeData('bs.' + that.type)\n      if (that.$tip) {\n        that.$tip.detach()\n      }\n      that.$tip = null\n      that.$arrow = null\n      that.$viewport = null\n      that.$element = null\n    })\n  }\n\n\n  // TOOLTIP PLUGIN DEFINITION\n  // =========================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.tooltip')\n      var options = typeof option == 'object' && option\n\n      if (!data && /destroy|hide/.test(option)) return\n      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  var old = $.fn.tooltip\n\n  $.fn.tooltip             = Plugin\n  $.fn.tooltip.Constructor = Tooltip\n\n\n  // TOOLTIP NO CONFLICT\n  // ===================\n\n  $.fn.tooltip.noConflict = function () {\n    $.fn.tooltip = old\n    return this\n  }\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: popover.js v3.3.7\n * http://getbootstrap.com/javascript/#popovers\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // POPOVER PUBLIC CLASS DEFINITION\n  // ===============================\n\n  var Popover = function (element, options) {\n    this.init('popover', element, options)\n  }\n\n  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')\n\n  Popover.VERSION  = '3.3.7'\n\n  Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {\n    placement: 'right',\n    trigger: 'click',\n    content: '',\n    template: '<div class=\"popover\" role=\"tooltip\"><div class=\"arrow\"></div><h3 class=\"popover-title\"></h3><div class=\"popover-content\"></div></div>'\n  })\n\n\n  // NOTE: POPOVER EXTENDS tooltip.js\n  // ================================\n\n  Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)\n\n  Popover.prototype.constructor = Popover\n\n  Popover.prototype.getDefaults = function () {\n    return Popover.DEFAULTS\n  }\n\n  Popover.prototype.setContent = function () {\n    var $tip    = this.tip()\n    var title   = this.getTitle()\n    var content = this.getContent()\n\n    $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)\n    $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events\n      this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'\n    ](content)\n\n    $tip.removeClass('fade top bottom left right in')\n\n    // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do\n    // this manually by checking the contents.\n    if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()\n  }\n\n  Popover.prototype.hasContent = function () {\n    return this.getTitle() || this.getContent()\n  }\n\n  Popover.prototype.getContent = function () {\n    var $e = this.$element\n    var o  = this.options\n\n    return $e.attr('data-content')\n      || (typeof o.content == 'function' ?\n            o.content.call($e[0]) :\n            o.content)\n  }\n\n  Popover.prototype.arrow = function () {\n    return (this.$arrow = this.$arrow || this.tip().find('.arrow'))\n  }\n\n\n  // POPOVER PLUGIN DEFINITION\n  // =========================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.popover')\n      var options = typeof option == 'object' && option\n\n      if (!data && /destroy|hide/.test(option)) return\n      if (!data) $this.data('bs.popover', (data = new Popover(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  var old = $.fn.popover\n\n  $.fn.popover             = Plugin\n  $.fn.popover.Constructor = Popover\n\n\n  // POPOVER NO CONFLICT\n  // ===================\n\n  $.fn.popover.noConflict = function () {\n    $.fn.popover = old\n    return this\n  }\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: scrollspy.js v3.3.7\n * http://getbootstrap.com/javascript/#scrollspy\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // SCROLLSPY CLASS DEFINITION\n  // ==========================\n\n  function ScrollSpy(element, options) {\n    this.$body          = $(document.body)\n    this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)\n    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)\n    this.selector       = (this.options.target || '') + ' .nav li > a'\n    this.offsets        = []\n    this.targets        = []\n    this.activeTarget   = null\n    this.scrollHeight   = 0\n\n    this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))\n    this.refresh()\n    this.process()\n  }\n\n  ScrollSpy.VERSION  = '3.3.7'\n\n  ScrollSpy.DEFAULTS = {\n    offset: 10\n  }\n\n  ScrollSpy.prototype.getScrollHeight = function () {\n    return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)\n  }\n\n  ScrollSpy.prototype.refresh = function () {\n    var that          = this\n    var offsetMethod  = 'offset'\n    var offsetBase    = 0\n\n    this.offsets      = []\n    this.targets      = []\n    this.scrollHeight = this.getScrollHeight()\n\n    if (!$.isWindow(this.$scrollElement[0])) {\n      offsetMethod = 'position'\n      offsetBase   = this.$scrollElement.scrollTop()\n    }\n\n    this.$body\n      .find(this.selector)\n      .map(function () {\n        var $el   = $(this)\n        var href  = $el.data('target') || $el.attr('href')\n        var $href = /^#./.test(href) && $(href)\n\n        return ($href\n          && $href.length\n          && $href.is(':visible')\n          && [[$href[offsetMethod]().top + offsetBase, href]]) || null\n      })\n      .sort(function (a, b) { return a[0] - b[0] })\n      .each(function () {\n        that.offsets.push(this[0])\n        that.targets.push(this[1])\n      })\n  }\n\n  ScrollSpy.prototype.process = function () {\n    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset\n    var scrollHeight = this.getScrollHeight()\n    var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height()\n    var offsets      = this.offsets\n    var targets      = this.targets\n    var activeTarget = this.activeTarget\n    var i\n\n    if (this.scrollHeight != scrollHeight) {\n      this.refresh()\n    }\n\n    if (scrollTop >= maxScroll) {\n      return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)\n    }\n\n    if (activeTarget && scrollTop < offsets[0]) {\n      this.activeTarget = null\n      return this.clear()\n    }\n\n    for (i = offsets.length; i--;) {\n      activeTarget != targets[i]\n        && scrollTop >= offsets[i]\n        && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])\n        && this.activate(targets[i])\n    }\n  }\n\n  ScrollSpy.prototype.activate = function (target) {\n    this.activeTarget = target\n\n    this.clear()\n\n    var selector = this.selector +\n      '[data-target=\"' + target + '\"],' +\n      this.selector + '[href=\"' + target + '\"]'\n\n    var active = $(selector)\n      .parents('li')\n      .addClass('active')\n\n    if (active.parent('.dropdown-menu').length) {\n      active = active\n        .closest('li.dropdown')\n        .addClass('active')\n    }\n\n    active.trigger('activate.bs.scrollspy')\n  }\n\n  ScrollSpy.prototype.clear = function () {\n    $(this.selector)\n      .parentsUntil(this.options.target, '.active')\n      .removeClass('active')\n  }\n\n\n  // SCROLLSPY PLUGIN DEFINITION\n  // ===========================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.scrollspy')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  var old = $.fn.scrollspy\n\n  $.fn.scrollspy             = Plugin\n  $.fn.scrollspy.Constructor = ScrollSpy\n\n\n  // SCROLLSPY NO CONFLICT\n  // =====================\n\n  $.fn.scrollspy.noConflict = function () {\n    $.fn.scrollspy = old\n    return this\n  }\n\n\n  // SCROLLSPY DATA-API\n  // ==================\n\n  $(window).on('load.bs.scrollspy.data-api', function () {\n    $('[data-spy=\"scroll\"]').each(function () {\n      var $spy = $(this)\n      Plugin.call($spy, $spy.data())\n    })\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: tab.js v3.3.7\n * http://getbootstrap.com/javascript/#tabs\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // TAB CLASS DEFINITION\n  // ====================\n\n  var Tab = function (element) {\n    // jscs:disable requireDollarBeforejQueryAssignment\n    this.element = $(element)\n    // jscs:enable requireDollarBeforejQueryAssignment\n  }\n\n  Tab.VERSION = '3.3.7'\n\n  Tab.TRANSITION_DURATION = 150\n\n  Tab.prototype.show = function () {\n    var $this    = this.element\n    var $ul      = $this.closest('ul:not(.dropdown-menu)')\n    var selector = $this.data('target')\n\n    if (!selector) {\n      selector = $this.attr('href')\n      selector = selector && selector.replace(/.*(?=#[^\\s]*$)/, '') // strip for ie7\n    }\n\n    if ($this.parent('li').hasClass('active')) return\n\n    var $previous = $ul.find('.active:last a')\n    var hideEvent = $.Event('hide.bs.tab', {\n      relatedTarget: $this[0]\n    })\n    var showEvent = $.Event('show.bs.tab', {\n      relatedTarget: $previous[0]\n    })\n\n    $previous.trigger(hideEvent)\n    $this.trigger(showEvent)\n\n    if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return\n\n    var $target = $(selector)\n\n    this.activate($this.closest('li'), $ul)\n    this.activate($target, $target.parent(), function () {\n      $previous.trigger({\n        type: 'hidden.bs.tab',\n        relatedTarget: $this[0]\n      })\n      $this.trigger({\n        type: 'shown.bs.tab',\n        relatedTarget: $previous[0]\n      })\n    })\n  }\n\n  Tab.prototype.activate = function (element, container, callback) {\n    var $active    = container.find('> .active')\n    var transition = callback\n      && $.support.transition\n      && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)\n\n    function next() {\n      $active\n        .removeClass('active')\n        .find('> .dropdown-menu > .active')\n          .removeClass('active')\n        .end()\n        .find('[data-toggle=\"tab\"]')\n          .attr('aria-expanded', false)\n\n      element\n        .addClass('active')\n        .find('[data-toggle=\"tab\"]')\n          .attr('aria-expanded', true)\n\n      if (transition) {\n        element[0].offsetWidth // reflow for transition\n        element.addClass('in')\n      } else {\n        element.removeClass('fade')\n      }\n\n      if (element.parent('.dropdown-menu').length) {\n        element\n          .closest('li.dropdown')\n            .addClass('active')\n          .end()\n          .find('[data-toggle=\"tab\"]')\n            .attr('aria-expanded', true)\n      }\n\n      callback && callback()\n    }\n\n    $active.length && transition ?\n      $active\n        .one('bsTransitionEnd', next)\n        .emulateTransitionEnd(Tab.TRANSITION_DURATION) :\n      next()\n\n    $active.removeClass('in')\n  }\n\n\n  // TAB PLUGIN DEFINITION\n  // =====================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this = $(this)\n      var data  = $this.data('bs.tab')\n\n      if (!data) $this.data('bs.tab', (data = new Tab(this)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  var old = $.fn.tab\n\n  $.fn.tab             = Plugin\n  $.fn.tab.Constructor = Tab\n\n\n  // TAB NO CONFLICT\n  // ===============\n\n  $.fn.tab.noConflict = function () {\n    $.fn.tab = old\n    return this\n  }\n\n\n  // TAB DATA-API\n  // ============\n\n  var clickHandler = function (e) {\n    e.preventDefault()\n    Plugin.call($(this), 'show')\n  }\n\n  $(document)\n    .on('click.bs.tab.data-api', '[data-toggle=\"tab\"]', clickHandler)\n    .on('click.bs.tab.data-api', '[data-toggle=\"pill\"]', clickHandler)\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: affix.js v3.3.7\n * http://getbootstrap.com/javascript/#affix\n * ========================================================================\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n  'use strict';\n\n  // AFFIX CLASS DEFINITION\n  // ======================\n\n  var Affix = function (element, options) {\n    this.options = $.extend({}, Affix.DEFAULTS, options)\n\n    this.$target = $(this.options.target)\n      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))\n      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))\n\n    this.$element     = $(element)\n    this.affixed      = null\n    this.unpin        = null\n    this.pinnedOffset = null\n\n    this.checkPosition()\n  }\n\n  Affix.VERSION  = '3.3.7'\n\n  Affix.RESET    = 'affix affix-top affix-bottom'\n\n  Affix.DEFAULTS = {\n    offset: 0,\n    target: window\n  }\n\n  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {\n    var scrollTop    = this.$target.scrollTop()\n    var position     = this.$element.offset()\n    var targetHeight = this.$target.height()\n\n    if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false\n\n    if (this.affixed == 'bottom') {\n      if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'\n      return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'\n    }\n\n    var initializing   = this.affixed == null\n    var colliderTop    = initializing ? scrollTop : position.top\n    var colliderHeight = initializing ? targetHeight : height\n\n    if (offsetTop != null && scrollTop <= offsetTop) return 'top'\n    if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'\n\n    return false\n  }\n\n  Affix.prototype.getPinnedOffset = function () {\n    if (this.pinnedOffset) return this.pinnedOffset\n    this.$element.removeClass(Affix.RESET).addClass('affix')\n    var scrollTop = this.$target.scrollTop()\n    var position  = this.$element.offset()\n    return (this.pinnedOffset = position.top - scrollTop)\n  }\n\n  Affix.prototype.checkPositionWithEventLoop = function () {\n    setTimeout($.proxy(this.checkPosition, this), 1)\n  }\n\n  Affix.prototype.checkPosition = function () {\n    if (!this.$element.is(':visible')) return\n\n    var height       = this.$element.height()\n    var offset       = this.options.offset\n    var offsetTop    = offset.top\n    var offsetBottom = offset.bottom\n    var scrollHeight = Math.max($(document).height(), $(document.body).height())\n\n    if (typeof offset != 'object')         offsetBottom = offsetTop = offset\n    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)\n    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)\n\n    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)\n\n    if (this.affixed != affix) {\n      if (this.unpin != null) this.$element.css('top', '')\n\n      var affixType = 'affix' + (affix ? '-' + affix : '')\n      var e         = $.Event(affixType + '.bs.affix')\n\n      this.$element.trigger(e)\n\n      if (e.isDefaultPrevented()) return\n\n      this.affixed = affix\n      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null\n\n      this.$element\n        .removeClass(Affix.RESET)\n        .addClass(affixType)\n        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')\n    }\n\n    if (affix == 'bottom') {\n      this.$element.offset({\n        top: scrollHeight - height - offsetBottom\n      })\n    }\n  }\n\n\n  // AFFIX PLUGIN DEFINITION\n  // =======================\n\n  function Plugin(option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.affix')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  var old = $.fn.affix\n\n  $.fn.affix             = Plugin\n  $.fn.affix.Constructor = Affix\n\n\n  // AFFIX NO CONFLICT\n  // =================\n\n  $.fn.affix.noConflict = function () {\n    $.fn.affix = old\n    return this\n  }\n\n\n  // AFFIX DATA-API\n  // ==============\n\n  $(window).on('load', function () {\n    $('[data-spy=\"affix\"]').each(function () {\n      var $spy = $(this)\n      var data = $spy.data()\n\n      data.offset = data.offset || {}\n\n      if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom\n      if (data.offsetTop    != null) data.offset.top    = data.offsetTop\n\n      Plugin.call($spy, data)\n    })\n  })\n\n}(jQuery);\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/bootstrap/dist/js/npm.js",
    "content": "// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.\nrequire('../../js/transition.js')\nrequire('../../js/alert.js')\nrequire('../../js/button.js')\nrequire('../../js/carousel.js')\nrequire('../../js/collapse.js')\nrequire('../../js/dropdown.js')\nrequire('../../js/modal.js')\nrequire('../../js/tooltip.js')\nrequire('../../js/popover.js')\nrequire('../../js/scrollspy.js')\nrequire('../../js/tab.js')\nrequire('../../js/affix.js')"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/jquery/.bower.json",
    "content": "{\n  \"name\": \"jquery\",\n  \"main\": \"dist/jquery.js\",\n  \"license\": \"MIT\",\n  \"ignore\": [\n    \"package.json\"\n  ],\n  \"keywords\": [\n    \"jquery\",\n    \"javascript\",\n    \"browser\",\n    \"library\"\n  ],\n  \"homepage\": \"https://github.com/jquery/jquery-dist\",\n  \"version\": \"2.2.0\",\n  \"_release\": \"2.2.0\",\n  \"_resolution\": {\n    \"type\": \"version\",\n    \"tag\": \"2.2.0\",\n    \"commit\": \"6fc01e29bdad0964f62ef56d01297039cdcadbe5\"\n  },\n  \"_source\": \"git://github.com/jquery/jquery-dist.git\",\n  \"_target\": \"2.2.0\",\n  \"_originalSource\": \"jquery\"\n}"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/jquery/LICENSE.txt",
    "content": "Copyright jQuery Foundation and other contributors, https://jquery.org/\n\nThis software consists of voluntary contributions made by many\nindividuals. For exact contribution history, see the revision history\navailable at https://github.com/jquery/jquery\n\nThe following license applies to all parts of this software except as\ndocumented below:\n\n====\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n====\n\nAll files located in the node_modules and external directories are\nexternally maintained libraries used by this software which have their\nown licenses; we recommend you read them, as their terms may differ from\nthe terms above.\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/jquery/dist/jquery.js",
    "content": "/*!\n * jQuery JavaScript Library v2.2.0\n * http://jquery.com/\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license\n * http://jquery.org/license\n *\n * Date: 2016-01-08T20:02Z\n */\n\n(function( global, factory ) {\n\n\tif ( typeof module === \"object\" && typeof module.exports === \"object\" ) {\n\t\t// For CommonJS and CommonJS-like environments where a proper `window`\n\t\t// is present, execute the factory and get jQuery.\n\t\t// For environments that do not have a `window` with a `document`\n\t\t// (such as Node.js), expose a factory as module.exports.\n\t\t// This accentuates the need for the creation of a real `window`.\n\t\t// e.g. var jQuery = require(\"jquery\")(window);\n\t\t// See ticket #14549 for more info.\n\t\tmodule.exports = global.document ?\n\t\t\tfactory( global, true ) :\n\t\t\tfunction( w ) {\n\t\t\t\tif ( !w.document ) {\n\t\t\t\t\tthrow new Error( \"jQuery requires a window with a document\" );\n\t\t\t\t}\n\t\t\t\treturn factory( w );\n\t\t\t};\n\t} else {\n\t\tfactory( global );\n\t}\n\n// Pass this if window is not defined yet\n}(typeof window !== \"undefined\" ? window : this, function( window, noGlobal ) {\n\n// Support: Firefox 18+\n// Can't be in strict mode, several libs including ASP.NET trace\n// the stack via arguments.caller.callee and Firefox dies if\n// you try to trace through \"use strict\" call chains. (#13335)\n//\"use strict\";\nvar arr = [];\n\nvar document = window.document;\n\nvar slice = arr.slice;\n\nvar concat = arr.concat;\n\nvar push = arr.push;\n\nvar indexOf = arr.indexOf;\n\nvar class2type = {};\n\nvar toString = class2type.toString;\n\nvar hasOwn = class2type.hasOwnProperty;\n\nvar support = {};\n\n\n\nvar\n\tversion = \"2.2.0\",\n\n\t// Define a local copy of jQuery\n\tjQuery = function( selector, context ) {\n\n\t\t// The jQuery object is actually just the init constructor 'enhanced'\n\t\t// Need init if jQuery is called (just allow error to be thrown if not included)\n\t\treturn new jQuery.fn.init( selector, context );\n\t},\n\n\t// Support: Android<4.1\n\t// Make sure we trim BOM and NBSP\n\trtrim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g,\n\n\t// Matches dashed string for camelizing\n\trmsPrefix = /^-ms-/,\n\trdashAlpha = /-([\\da-z])/gi,\n\n\t// Used by jQuery.camelCase as callback to replace()\n\tfcamelCase = function( all, letter ) {\n\t\treturn letter.toUpperCase();\n\t};\n\njQuery.fn = jQuery.prototype = {\n\n\t// The current version of jQuery being used\n\tjquery: version,\n\n\tconstructor: jQuery,\n\n\t// Start with an empty selector\n\tselector: \"\",\n\n\t// The default length of a jQuery object is 0\n\tlength: 0,\n\n\ttoArray: function() {\n\t\treturn slice.call( this );\n\t},\n\n\t// Get the Nth element in the matched element set OR\n\t// Get the whole matched element set as a clean array\n\tget: function( num ) {\n\t\treturn num != null ?\n\n\t\t\t// Return just the one element from the set\n\t\t\t( num < 0 ? this[ num + this.length ] : this[ num ] ) :\n\n\t\t\t// Return all the elements in a clean array\n\t\t\tslice.call( this );\n\t},\n\n\t// Take an array of elements and push it onto the stack\n\t// (returning the new matched element set)\n\tpushStack: function( elems ) {\n\n\t\t// Build a new jQuery matched element set\n\t\tvar ret = jQuery.merge( this.constructor(), elems );\n\n\t\t// Add the old object onto the stack (as a reference)\n\t\tret.prevObject = this;\n\t\tret.context = this.context;\n\n\t\t// Return the newly-formed element set\n\t\treturn ret;\n\t},\n\n\t// Execute a callback for every element in the matched set.\n\teach: function( callback ) {\n\t\treturn jQuery.each( this, callback );\n\t},\n\n\tmap: function( callback ) {\n\t\treturn this.pushStack( jQuery.map( this, function( elem, i ) {\n\t\t\treturn callback.call( elem, i, elem );\n\t\t} ) );\n\t},\n\n\tslice: function() {\n\t\treturn this.pushStack( slice.apply( this, arguments ) );\n\t},\n\n\tfirst: function() {\n\t\treturn this.eq( 0 );\n\t},\n\n\tlast: function() {\n\t\treturn this.eq( -1 );\n\t},\n\n\teq: function( i ) {\n\t\tvar len = this.length,\n\t\t\tj = +i + ( i < 0 ? len : 0 );\n\t\treturn this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );\n\t},\n\n\tend: function() {\n\t\treturn this.prevObject || this.constructor();\n\t},\n\n\t// For internal use only.\n\t// Behaves like an Array's method, not like a jQuery method.\n\tpush: push,\n\tsort: arr.sort,\n\tsplice: arr.splice\n};\n\njQuery.extend = jQuery.fn.extend = function() {\n\tvar options, name, src, copy, copyIsArray, clone,\n\t\ttarget = arguments[ 0 ] || {},\n\t\ti = 1,\n\t\tlength = arguments.length,\n\t\tdeep = false;\n\n\t// Handle a deep copy situation\n\tif ( typeof target === \"boolean\" ) {\n\t\tdeep = target;\n\n\t\t// Skip the boolean and the target\n\t\ttarget = arguments[ i ] || {};\n\t\ti++;\n\t}\n\n\t// Handle case when target is a string or something (possible in deep copy)\n\tif ( typeof target !== \"object\" && !jQuery.isFunction( target ) ) {\n\t\ttarget = {};\n\t}\n\n\t// Extend jQuery itself if only one argument is passed\n\tif ( i === length ) {\n\t\ttarget = this;\n\t\ti--;\n\t}\n\n\tfor ( ; i < length; i++ ) {\n\n\t\t// Only deal with non-null/undefined values\n\t\tif ( ( options = arguments[ i ] ) != null ) {\n\n\t\t\t// Extend the base object\n\t\t\tfor ( name in options ) {\n\t\t\t\tsrc = target[ name ];\n\t\t\t\tcopy = options[ name ];\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif ( target === copy ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\tif ( deep && copy && ( jQuery.isPlainObject( copy ) ||\n\t\t\t\t\t( copyIsArray = jQuery.isArray( copy ) ) ) ) {\n\n\t\t\t\t\tif ( copyIsArray ) {\n\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\tclone = src && jQuery.isArray( src ) ? src : [];\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclone = src && jQuery.isPlainObject( src ) ? src : {};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\ttarget[ name ] = jQuery.extend( deep, clone, copy );\n\n\t\t\t\t// Don't bring in undefined values\n\t\t\t\t} else if ( copy !== undefined ) {\n\t\t\t\t\ttarget[ name ] = copy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\njQuery.extend( {\n\n\t// Unique for each copy of jQuery on the page\n\texpando: \"jQuery\" + ( version + Math.random() ).replace( /\\D/g, \"\" ),\n\n\t// Assume jQuery is ready without the ready module\n\tisReady: true,\n\n\terror: function( msg ) {\n\t\tthrow new Error( msg );\n\t},\n\n\tnoop: function() {},\n\n\tisFunction: function( obj ) {\n\t\treturn jQuery.type( obj ) === \"function\";\n\t},\n\n\tisArray: Array.isArray,\n\n\tisWindow: function( obj ) {\n\t\treturn obj != null && obj === obj.window;\n\t},\n\n\tisNumeric: function( obj ) {\n\n\t\t// parseFloat NaNs numeric-cast false positives (null|true|false|\"\")\n\t\t// ...but misinterprets leading-number strings, particularly hex literals (\"0x...\")\n\t\t// subtraction forces infinities to NaN\n\t\t// adding 1 corrects loss of precision from parseFloat (#15100)\n\t\tvar realStringObj = obj && obj.toString();\n\t\treturn !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0;\n\t},\n\n\tisPlainObject: function( obj ) {\n\n\t\t// Not plain objects:\n\t\t// - Any object or value whose internal [[Class]] property is not \"[object Object]\"\n\t\t// - DOM nodes\n\t\t// - window\n\t\tif ( jQuery.type( obj ) !== \"object\" || obj.nodeType || jQuery.isWindow( obj ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif ( obj.constructor &&\n\t\t\t\t!hasOwn.call( obj.constructor.prototype, \"isPrototypeOf\" ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// If the function hasn't returned already, we're confident that\n\t\t// |obj| is a plain object, created by {} or constructed with new Object\n\t\treturn true;\n\t},\n\n\tisEmptyObject: function( obj ) {\n\t\tvar name;\n\t\tfor ( name in obj ) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\ttype: function( obj ) {\n\t\tif ( obj == null ) {\n\t\t\treturn obj + \"\";\n\t\t}\n\n\t\t// Support: Android<4.0, iOS<6 (functionish RegExp)\n\t\treturn typeof obj === \"object\" || typeof obj === \"function\" ?\n\t\t\tclass2type[ toString.call( obj ) ] || \"object\" :\n\t\t\ttypeof obj;\n\t},\n\n\t// Evaluates a script in a global context\n\tglobalEval: function( code ) {\n\t\tvar script,\n\t\t\tindirect = eval;\n\n\t\tcode = jQuery.trim( code );\n\n\t\tif ( code ) {\n\n\t\t\t// If the code includes a valid, prologue position\n\t\t\t// strict mode pragma, execute code by injecting a\n\t\t\t// script tag into the document.\n\t\t\tif ( code.indexOf( \"use strict\" ) === 1 ) {\n\t\t\t\tscript = document.createElement( \"script\" );\n\t\t\t\tscript.text = code;\n\t\t\t\tdocument.head.appendChild( script ).parentNode.removeChild( script );\n\t\t\t} else {\n\n\t\t\t\t// Otherwise, avoid the DOM node creation, insertion\n\t\t\t\t// and removal by using an indirect global eval\n\n\t\t\t\tindirect( code );\n\t\t\t}\n\t\t}\n\t},\n\n\t// Convert dashed to camelCase; used by the css and data modules\n\t// Support: IE9-11+\n\t// Microsoft forgot to hump their vendor prefix (#9572)\n\tcamelCase: function( string ) {\n\t\treturn string.replace( rmsPrefix, \"ms-\" ).replace( rdashAlpha, fcamelCase );\n\t},\n\n\tnodeName: function( elem, name ) {\n\t\treturn elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();\n\t},\n\n\teach: function( obj, callback ) {\n\t\tvar length, i = 0;\n\n\t\tif ( isArrayLike( obj ) ) {\n\t\t\tlength = obj.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor ( i in obj ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn obj;\n\t},\n\n\t// Support: Android<4.1\n\ttrim: function( text ) {\n\t\treturn text == null ?\n\t\t\t\"\" :\n\t\t\t( text + \"\" ).replace( rtrim, \"\" );\n\t},\n\n\t// results is for internal usage only\n\tmakeArray: function( arr, results ) {\n\t\tvar ret = results || [];\n\n\t\tif ( arr != null ) {\n\t\t\tif ( isArrayLike( Object( arr ) ) ) {\n\t\t\t\tjQuery.merge( ret,\n\t\t\t\t\ttypeof arr === \"string\" ?\n\t\t\t\t\t[ arr ] : arr\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tpush.call( ret, arr );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tinArray: function( elem, arr, i ) {\n\t\treturn arr == null ? -1 : indexOf.call( arr, elem, i );\n\t},\n\n\tmerge: function( first, second ) {\n\t\tvar len = +second.length,\n\t\t\tj = 0,\n\t\t\ti = first.length;\n\n\t\tfor ( ; j < len; j++ ) {\n\t\t\tfirst[ i++ ] = second[ j ];\n\t\t}\n\n\t\tfirst.length = i;\n\n\t\treturn first;\n\t},\n\n\tgrep: function( elems, callback, invert ) {\n\t\tvar callbackInverse,\n\t\t\tmatches = [],\n\t\t\ti = 0,\n\t\t\tlength = elems.length,\n\t\t\tcallbackExpect = !invert;\n\n\t\t// Go through the array, only saving the items\n\t\t// that pass the validator function\n\t\tfor ( ; i < length; i++ ) {\n\t\t\tcallbackInverse = !callback( elems[ i ], i );\n\t\t\tif ( callbackInverse !== callbackExpect ) {\n\t\t\t\tmatches.push( elems[ i ] );\n\t\t\t}\n\t\t}\n\n\t\treturn matches;\n\t},\n\n\t// arg is for internal usage only\n\tmap: function( elems, callback, arg ) {\n\t\tvar length, value,\n\t\t\ti = 0,\n\t\t\tret = [];\n\n\t\t// Go through the array, translating each of the items to their new values\n\t\tif ( isArrayLike( elems ) ) {\n\t\t\tlength = elems.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Go through every key on the object,\n\t\t} else {\n\t\t\tfor ( i in elems ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Flatten any nested arrays\n\t\treturn concat.apply( [], ret );\n\t},\n\n\t// A global GUID counter for objects\n\tguid: 1,\n\n\t// Bind a function to a context, optionally partially applying any\n\t// arguments.\n\tproxy: function( fn, context ) {\n\t\tvar tmp, args, proxy;\n\n\t\tif ( typeof context === \"string\" ) {\n\t\t\ttmp = fn[ context ];\n\t\t\tcontext = fn;\n\t\t\tfn = tmp;\n\t\t}\n\n\t\t// Quick check to determine if target is callable, in the spec\n\t\t// this throws a TypeError, but we will just return undefined.\n\t\tif ( !jQuery.isFunction( fn ) ) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Simulated bind\n\t\targs = slice.call( arguments, 2 );\n\t\tproxy = function() {\n\t\t\treturn fn.apply( context || this, args.concat( slice.call( arguments ) ) );\n\t\t};\n\n\t\t// Set the guid of unique handler to the same of original handler, so it can be removed\n\t\tproxy.guid = fn.guid = fn.guid || jQuery.guid++;\n\n\t\treturn proxy;\n\t},\n\n\tnow: Date.now,\n\n\t// jQuery.support is not used in Core but other projects attach their\n\t// properties to it so it needs to exist.\n\tsupport: support\n} );\n\n// JSHint would error on this code due to the Symbol not being defined in ES5.\n// Defining this global in .jshintrc would create a danger of using the global\n// unguarded in another place, it seems safer to just disable JSHint for these\n// three lines.\n/* jshint ignore: start */\nif ( typeof Symbol === \"function\" ) {\n\tjQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];\n}\n/* jshint ignore: end */\n\n// Populate the class2type map\njQuery.each( \"Boolean Number String Function Array Date RegExp Object Error Symbol\".split( \" \" ),\nfunction( i, name ) {\n\tclass2type[ \"[object \" + name + \"]\" ] = name.toLowerCase();\n} );\n\nfunction isArrayLike( obj ) {\n\n\t// Support: iOS 8.2 (not reproducible in simulator)\n\t// `in` check used to prevent JIT error (gh-2145)\n\t// hasOwn isn't used here due to false negatives\n\t// regarding Nodelist length in IE\n\tvar length = !!obj && \"length\" in obj && obj.length,\n\t\ttype = jQuery.type( obj );\n\n\tif ( type === \"function\" || jQuery.isWindow( obj ) ) {\n\t\treturn false;\n\t}\n\n\treturn type === \"array\" || length === 0 ||\n\t\ttypeof length === \"number\" && length > 0 && ( length - 1 ) in obj;\n}\nvar Sizzle =\n/*!\n * Sizzle CSS Selector Engine v2.2.1\n * http://sizzlejs.com/\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license\n * http://jquery.org/license\n *\n * Date: 2015-10-17\n */\n(function( window ) {\n\nvar i,\n\tsupport,\n\tExpr,\n\tgetText,\n\tisXML,\n\ttokenize,\n\tcompile,\n\tselect,\n\toutermostContext,\n\tsortInput,\n\thasDuplicate,\n\n\t// Local document vars\n\tsetDocument,\n\tdocument,\n\tdocElem,\n\tdocumentIsHTML,\n\trbuggyQSA,\n\trbuggyMatches,\n\tmatches,\n\tcontains,\n\n\t// Instance-specific data\n\texpando = \"sizzle\" + 1 * new Date(),\n\tpreferredDoc = window.document,\n\tdirruns = 0,\n\tdone = 0,\n\tclassCache = createCache(),\n\ttokenCache = createCache(),\n\tcompilerCache = createCache(),\n\tsortOrder = function( a, b ) {\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn 0;\n\t},\n\n\t// General-purpose constants\n\tMAX_NEGATIVE = 1 << 31,\n\n\t// Instance methods\n\thasOwn = ({}).hasOwnProperty,\n\tarr = [],\n\tpop = arr.pop,\n\tpush_native = arr.push,\n\tpush = arr.push,\n\tslice = arr.slice,\n\t// Use a stripped-down indexOf as it's faster than native\n\t// http://jsperf.com/thor-indexof-vs-for/5\n\tindexOf = function( list, elem ) {\n\t\tvar i = 0,\n\t\t\tlen = list.length;\n\t\tfor ( ; i < len; i++ ) {\n\t\t\tif ( list[i] === elem ) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t},\n\n\tbooleans = \"checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped\",\n\n\t// Regular expressions\n\n\t// http://www.w3.org/TR/css3-selectors/#whitespace\n\twhitespace = \"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",\n\n\t// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier\n\tidentifier = \"(?:\\\\\\\\.|[\\\\w-]|[^\\\\x00-\\\\xa0])+\",\n\n\t// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors\n\tattributes = \"\\\\[\" + whitespace + \"*(\" + identifier + \")(?:\" + whitespace +\n\t\t// Operator (capture 2)\n\t\t\"*([*^$|!~]?=)\" + whitespace +\n\t\t// \"Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]\"\n\t\t\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\" + identifier + \"))|)\" + whitespace +\n\t\t\"*\\\\]\",\n\n\tpseudos = \":(\" + identifier + \")(?:\\\\((\" +\n\t\t// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:\n\t\t// 1. quoted (capture 3; capture 4 or capture 5)\n\t\t\"('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|\" +\n\t\t// 2. simple (capture 6)\n\t\t\"((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\" + attributes + \")*)|\" +\n\t\t// 3. anything else (capture 2)\n\t\t\".*\" +\n\t\t\")\\\\)|)\",\n\n\t// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter\n\trwhitespace = new RegExp( whitespace + \"+\", \"g\" ),\n\trtrim = new RegExp( \"^\" + whitespace + \"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\" + whitespace + \"+$\", \"g\" ),\n\n\trcomma = new RegExp( \"^\" + whitespace + \"*,\" + whitespace + \"*\" ),\n\trcombinators = new RegExp( \"^\" + whitespace + \"*([>+~]|\" + whitespace + \")\" + whitespace + \"*\" ),\n\n\trattributeQuotes = new RegExp( \"=\" + whitespace + \"*([^\\\\]'\\\"]*?)\" + whitespace + \"*\\\\]\", \"g\" ),\n\n\trpseudo = new RegExp( pseudos ),\n\tridentifier = new RegExp( \"^\" + identifier + \"$\" ),\n\n\tmatchExpr = {\n\t\t\"ID\": new RegExp( \"^#(\" + identifier + \")\" ),\n\t\t\"CLASS\": new RegExp( \"^\\\\.(\" + identifier + \")\" ),\n\t\t\"TAG\": new RegExp( \"^(\" + identifier + \"|[*])\" ),\n\t\t\"ATTR\": new RegExp( \"^\" + attributes ),\n\t\t\"PSEUDO\": new RegExp( \"^\" + pseudos ),\n\t\t\"CHILD\": new RegExp( \"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\" + whitespace +\n\t\t\t\"*(even|odd|(([+-]|)(\\\\d*)n|)\" + whitespace + \"*(?:([+-]|)\" + whitespace +\n\t\t\t\"*(\\\\d+)|))\" + whitespace + \"*\\\\)|)\", \"i\" ),\n\t\t\"bool\": new RegExp( \"^(?:\" + booleans + \")$\", \"i\" ),\n\t\t// For use in libraries implementing .is()\n\t\t// We use this for POS matching in `select`\n\t\t\"needsContext\": new RegExp( \"^\" + whitespace + \"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\" +\n\t\t\twhitespace + \"*((?:-\\\\d)?\\\\d*)\" + whitespace + \"*\\\\)|)(?=[^-]|$)\", \"i\" )\n\t},\n\n\trinputs = /^(?:input|select|textarea|button)$/i,\n\trheader = /^h\\d$/i,\n\n\trnative = /^[^{]+\\{\\s*\\[native \\w/,\n\n\t// Easily-parseable/retrievable ID or TAG or CLASS selectors\n\trquickExpr = /^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,\n\n\trsibling = /[+~]/,\n\trescape = /'|\\\\/g,\n\n\t// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters\n\trunescape = new RegExp( \"\\\\\\\\([\\\\da-f]{1,6}\" + whitespace + \"?|(\" + whitespace + \")|.)\", \"ig\" ),\n\tfunescape = function( _, escaped, escapedWhitespace ) {\n\t\tvar high = \"0x\" + escaped - 0x10000;\n\t\t// NaN means non-codepoint\n\t\t// Support: Firefox<24\n\t\t// Workaround erroneous numeric interpretation of +\"0x\"\n\t\treturn high !== high || escapedWhitespace ?\n\t\t\tescaped :\n\t\t\thigh < 0 ?\n\t\t\t\t// BMP codepoint\n\t\t\t\tString.fromCharCode( high + 0x10000 ) :\n\t\t\t\t// Supplemental Plane codepoint (surrogate pair)\n\t\t\t\tString.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );\n\t},\n\n\t// Used for iframes\n\t// See setDocument()\n\t// Removing the function wrapper causes a \"Permission Denied\"\n\t// error in IE\n\tunloadHandler = function() {\n\t\tsetDocument();\n\t};\n\n// Optimize for push.apply( _, NodeList )\ntry {\n\tpush.apply(\n\t\t(arr = slice.call( preferredDoc.childNodes )),\n\t\tpreferredDoc.childNodes\n\t);\n\t// Support: Android<4.0\n\t// Detect silently failing push.apply\n\tarr[ preferredDoc.childNodes.length ].nodeType;\n} catch ( e ) {\n\tpush = { apply: arr.length ?\n\n\t\t// Leverage slice if possible\n\t\tfunction( target, els ) {\n\t\t\tpush_native.apply( target, slice.call(els) );\n\t\t} :\n\n\t\t// Support: IE<9\n\t\t// Otherwise append directly\n\t\tfunction( target, els ) {\n\t\t\tvar j = target.length,\n\t\t\t\ti = 0;\n\t\t\t// Can't trust NodeList.length\n\t\t\twhile ( (target[j++] = els[i++]) ) {}\n\t\t\ttarget.length = j - 1;\n\t\t}\n\t};\n}\n\nfunction Sizzle( selector, context, results, seed ) {\n\tvar m, i, elem, nid, nidselect, match, groups, newSelector,\n\t\tnewContext = context && context.ownerDocument,\n\n\t\t// nodeType defaults to 9, since context defaults to document\n\t\tnodeType = context ? context.nodeType : 9;\n\n\tresults = results || [];\n\n\t// Return early from calls with invalid selector or context\n\tif ( typeof selector !== \"string\" || !selector ||\n\t\tnodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {\n\n\t\treturn results;\n\t}\n\n\t// Try to shortcut find operations (as opposed to filters) in HTML documents\n\tif ( !seed ) {\n\n\t\tif ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {\n\t\t\tsetDocument( context );\n\t\t}\n\t\tcontext = context || document;\n\n\t\tif ( documentIsHTML ) {\n\n\t\t\t// If the selector is sufficiently simple, try using a \"get*By*\" DOM method\n\t\t\t// (excepting DocumentFragment context, where the methods don't exist)\n\t\t\tif ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {\n\n\t\t\t\t// ID selector\n\t\t\t\tif ( (m = match[1]) ) {\n\n\t\t\t\t\t// Document context\n\t\t\t\t\tif ( nodeType === 9 ) {\n\t\t\t\t\t\tif ( (elem = context.getElementById( m )) ) {\n\n\t\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\t\tif ( elem.id === m ) {\n\t\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t// Element context\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\tif ( newContext && (elem = newContext.getElementById( m )) &&\n\t\t\t\t\t\t\tcontains( context, elem ) &&\n\t\t\t\t\t\t\telem.id === m ) {\n\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t// Type selector\n\t\t\t\t} else if ( match[2] ) {\n\t\t\t\t\tpush.apply( results, context.getElementsByTagName( selector ) );\n\t\t\t\t\treturn results;\n\n\t\t\t\t// Class selector\n\t\t\t\t} else if ( (m = match[3]) && support.getElementsByClassName &&\n\t\t\t\t\tcontext.getElementsByClassName ) {\n\n\t\t\t\t\tpush.apply( results, context.getElementsByClassName( m ) );\n\t\t\t\t\treturn results;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Take advantage of querySelectorAll\n\t\t\tif ( support.qsa &&\n\t\t\t\t!compilerCache[ selector + \" \" ] &&\n\t\t\t\t(!rbuggyQSA || !rbuggyQSA.test( selector )) ) {\n\n\t\t\t\tif ( nodeType !== 1 ) {\n\t\t\t\t\tnewContext = context;\n\t\t\t\t\tnewSelector = selector;\n\n\t\t\t\t// qSA looks outside Element context, which is not what we want\n\t\t\t\t// Thanks to Andrew Dupont for this workaround technique\n\t\t\t\t// Support: IE <=8\n\t\t\t\t// Exclude object elements\n\t\t\t\t} else if ( context.nodeName.toLowerCase() !== \"object\" ) {\n\n\t\t\t\t\t// Capture the context ID, setting it first if necessary\n\t\t\t\t\tif ( (nid = context.getAttribute( \"id\" )) ) {\n\t\t\t\t\t\tnid = nid.replace( rescape, \"\\\\$&\" );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontext.setAttribute( \"id\", (nid = expando) );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prefix every selector in the list\n\t\t\t\t\tgroups = tokenize( selector );\n\t\t\t\t\ti = groups.length;\n\t\t\t\t\tnidselect = ridentifier.test( nid ) ? \"#\" + nid : \"[id='\" + nid + \"']\";\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tgroups[i] = nidselect + \" \" + toSelector( groups[i] );\n\t\t\t\t\t}\n\t\t\t\t\tnewSelector = groups.join( \",\" );\n\n\t\t\t\t\t// Expand context for sibling selectors\n\t\t\t\t\tnewContext = rsibling.test( selector ) && testContext( context.parentNode ) ||\n\t\t\t\t\t\tcontext;\n\t\t\t\t}\n\n\t\t\t\tif ( newSelector ) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tpush.apply( results,\n\t\t\t\t\t\t\tnewContext.querySelectorAll( newSelector )\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn results;\n\t\t\t\t\t} catch ( qsaError ) {\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif ( nid === expando ) {\n\t\t\t\t\t\t\tcontext.removeAttribute( \"id\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// All others\n\treturn select( selector.replace( rtrim, \"$1\" ), context, results, seed );\n}\n\n/**\n * Create key-value caches of limited size\n * @returns {function(string, object)} Returns the Object data after storing it on itself with\n *\tproperty name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)\n *\tdeleting the oldest entry\n */\nfunction createCache() {\n\tvar keys = [];\n\n\tfunction cache( key, value ) {\n\t\t// Use (key + \" \") to avoid collision with native prototype properties (see Issue #157)\n\t\tif ( keys.push( key + \" \" ) > Expr.cacheLength ) {\n\t\t\t// Only keep the most recent entries\n\t\t\tdelete cache[ keys.shift() ];\n\t\t}\n\t\treturn (cache[ key + \" \" ] = value);\n\t}\n\treturn cache;\n}\n\n/**\n * Mark a function for special use by Sizzle\n * @param {Function} fn The function to mark\n */\nfunction markFunction( fn ) {\n\tfn[ expando ] = true;\n\treturn fn;\n}\n\n/**\n * Support testing using an element\n * @param {Function} fn Passed the created div and expects a boolean result\n */\nfunction assert( fn ) {\n\tvar div = document.createElement(\"div\");\n\n\ttry {\n\t\treturn !!fn( div );\n\t} catch (e) {\n\t\treturn false;\n\t} finally {\n\t\t// Remove from its parent by default\n\t\tif ( div.parentNode ) {\n\t\t\tdiv.parentNode.removeChild( div );\n\t\t}\n\t\t// release memory in IE\n\t\tdiv = null;\n\t}\n}\n\n/**\n * Adds the same handler for all of the specified attrs\n * @param {String} attrs Pipe-separated list of attributes\n * @param {Function} handler The method that will be applied\n */\nfunction addHandle( attrs, handler ) {\n\tvar arr = attrs.split(\"|\"),\n\t\ti = arr.length;\n\n\twhile ( i-- ) {\n\t\tExpr.attrHandle[ arr[i] ] = handler;\n\t}\n}\n\n/**\n * Checks document order of two siblings\n * @param {Element} a\n * @param {Element} b\n * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b\n */\nfunction siblingCheck( a, b ) {\n\tvar cur = b && a,\n\t\tdiff = cur && a.nodeType === 1 && b.nodeType === 1 &&\n\t\t\t( ~b.sourceIndex || MAX_NEGATIVE ) -\n\t\t\t( ~a.sourceIndex || MAX_NEGATIVE );\n\n\t// Use IE sourceIndex if available on both nodes\n\tif ( diff ) {\n\t\treturn diff;\n\t}\n\n\t// Check if b follows a\n\tif ( cur ) {\n\t\twhile ( (cur = cur.nextSibling) ) {\n\t\t\tif ( cur === b ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn a ? 1 : -1;\n}\n\n/**\n * Returns a function to use in pseudos for input types\n * @param {String} type\n */\nfunction createInputPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn name === \"input\" && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for buttons\n * @param {String} type\n */\nfunction createButtonPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn (name === \"input\" || name === \"button\") && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for positionals\n * @param {Function} fn\n */\nfunction createPositionalPseudo( fn ) {\n\treturn markFunction(function( argument ) {\n\t\targument = +argument;\n\t\treturn markFunction(function( seed, matches ) {\n\t\t\tvar j,\n\t\t\t\tmatchIndexes = fn( [], seed.length, argument ),\n\t\t\t\ti = matchIndexes.length;\n\n\t\t\t// Match elements found at the specified indexes\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( seed[ (j = matchIndexes[i]) ] ) {\n\t\t\t\t\tseed[j] = !(matches[j] = seed[j]);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t});\n}\n\n/**\n * Checks a node for validity as a Sizzle context\n * @param {Element|Object=} context\n * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value\n */\nfunction testContext( context ) {\n\treturn context && typeof context.getElementsByTagName !== \"undefined\" && context;\n}\n\n// Expose support vars for convenience\nsupport = Sizzle.support = {};\n\n/**\n * Detects XML nodes\n * @param {Element|Object} elem An element or a document\n * @returns {Boolean} True iff elem is a non-HTML XML node\n */\nisXML = Sizzle.isXML = function( elem ) {\n\t// documentElement is verified for cases where it doesn't yet exist\n\t// (such as loading iframes in IE - #4833)\n\tvar documentElement = elem && (elem.ownerDocument || elem).documentElement;\n\treturn documentElement ? documentElement.nodeName !== \"HTML\" : false;\n};\n\n/**\n * Sets document-related variables once based on the current document\n * @param {Element|Object} [doc] An element or document object to use to set the document\n * @returns {Object} Returns the current document\n */\nsetDocument = Sizzle.setDocument = function( node ) {\n\tvar hasCompare, parent,\n\t\tdoc = node ? node.ownerDocument || node : preferredDoc;\n\n\t// Return early if doc is invalid or already selected\n\tif ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {\n\t\treturn document;\n\t}\n\n\t// Update global variables\n\tdocument = doc;\n\tdocElem = document.documentElement;\n\tdocumentIsHTML = !isXML( document );\n\n\t// Support: IE 9-11, Edge\n\t// Accessing iframe documents after unload throws \"permission denied\" errors (jQuery #13936)\n\tif ( (parent = document.defaultView) && parent.top !== parent ) {\n\t\t// Support: IE 11\n\t\tif ( parent.addEventListener ) {\n\t\t\tparent.addEventListener( \"unload\", unloadHandler, false );\n\n\t\t// Support: IE 9 - 10 only\n\t\t} else if ( parent.attachEvent ) {\n\t\t\tparent.attachEvent( \"onunload\", unloadHandler );\n\t\t}\n\t}\n\n\t/* Attributes\n\t---------------------------------------------------------------------- */\n\n\t// Support: IE<8\n\t// Verify that getAttribute really returns attributes and not properties\n\t// (excepting IE8 booleans)\n\tsupport.attributes = assert(function( div ) {\n\t\tdiv.className = \"i\";\n\t\treturn !div.getAttribute(\"className\");\n\t});\n\n\t/* getElement(s)By*\n\t---------------------------------------------------------------------- */\n\n\t// Check if getElementsByTagName(\"*\") returns only elements\n\tsupport.getElementsByTagName = assert(function( div ) {\n\t\tdiv.appendChild( document.createComment(\"\") );\n\t\treturn !div.getElementsByTagName(\"*\").length;\n\t});\n\n\t// Support: IE<9\n\tsupport.getElementsByClassName = rnative.test( document.getElementsByClassName );\n\n\t// Support: IE<10\n\t// Check if getElementById returns elements by name\n\t// The broken getElementById methods don't pick up programatically-set names,\n\t// so use a roundabout getElementsByName test\n\tsupport.getById = assert(function( div ) {\n\t\tdocElem.appendChild( div ).id = expando;\n\t\treturn !document.getElementsByName || !document.getElementsByName( expando ).length;\n\t});\n\n\t// ID find and filter\n\tif ( support.getById ) {\n\t\tExpr.find[\"ID\"] = function( id, context ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && documentIsHTML ) {\n\t\t\t\tvar m = context.getElementById( id );\n\t\t\t\treturn m ? [ m ] : [];\n\t\t\t}\n\t\t};\n\t\tExpr.filter[\"ID\"] = function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn elem.getAttribute(\"id\") === attrId;\n\t\t\t};\n\t\t};\n\t} else {\n\t\t// Support: IE6/7\n\t\t// getElementById is not reliable as a find shortcut\n\t\tdelete Expr.find[\"ID\"];\n\n\t\tExpr.filter[\"ID\"] =  function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\tvar node = typeof elem.getAttributeNode !== \"undefined\" &&\n\t\t\t\t\telem.getAttributeNode(\"id\");\n\t\t\t\treturn node && node.value === attrId;\n\t\t\t};\n\t\t};\n\t}\n\n\t// Tag\n\tExpr.find[\"TAG\"] = support.getElementsByTagName ?\n\t\tfunction( tag, context ) {\n\t\t\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\t\t\treturn context.getElementsByTagName( tag );\n\n\t\t\t// DocumentFragment nodes don't have gEBTN\n\t\t\t} else if ( support.qsa ) {\n\t\t\t\treturn context.querySelectorAll( tag );\n\t\t\t}\n\t\t} :\n\n\t\tfunction( tag, context ) {\n\t\t\tvar elem,\n\t\t\t\ttmp = [],\n\t\t\t\ti = 0,\n\t\t\t\t// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too\n\t\t\t\tresults = context.getElementsByTagName( tag );\n\n\t\t\t// Filter out possible comments\n\t\t\tif ( tag === \"*\" ) {\n\t\t\t\twhile ( (elem = results[i++]) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\ttmp.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn tmp;\n\t\t\t}\n\t\t\treturn results;\n\t\t};\n\n\t// Class\n\tExpr.find[\"CLASS\"] = support.getElementsByClassName && function( className, context ) {\n\t\tif ( typeof context.getElementsByClassName !== \"undefined\" && documentIsHTML ) {\n\t\t\treturn context.getElementsByClassName( className );\n\t\t}\n\t};\n\n\t/* QSA/matchesSelector\n\t---------------------------------------------------------------------- */\n\n\t// QSA and matchesSelector support\n\n\t// matchesSelector(:active) reports false when true (IE9/Opera 11.5)\n\trbuggyMatches = [];\n\n\t// qSa(:focus) reports false when true (Chrome 21)\n\t// We allow this because of a bug in IE8/9 that throws an error\n\t// whenever `document.activeElement` is accessed on an iframe\n\t// So, we allow :focus to pass through QSA all the time to avoid the IE error\n\t// See http://bugs.jquery.com/ticket/13378\n\trbuggyQSA = [];\n\n\tif ( (support.qsa = rnative.test( document.querySelectorAll )) ) {\n\t\t// Build QSA regex\n\t\t// Regex strategy adopted from Diego Perini\n\t\tassert(function( div ) {\n\t\t\t// Select is set to empty string on purpose\n\t\t\t// This is to test IE's treatment of not explicitly\n\t\t\t// setting a boolean content attribute,\n\t\t\t// since its presence should be enough\n\t\t\t// http://bugs.jquery.com/ticket/12359\n\t\t\tdocElem.appendChild( div ).innerHTML = \"<a id='\" + expando + \"'></a>\" +\n\t\t\t\t\"<select id='\" + expando + \"-\\r\\\\' msallowcapture=''>\" +\n\t\t\t\t\"<option selected=''></option></select>\";\n\n\t\t\t// Support: IE8, Opera 11-12.16\n\t\t\t// Nothing should be selected when empty strings follow ^= or $= or *=\n\t\t\t// The test attribute must be unknown in Opera but \"safe\" for WinRT\n\t\t\t// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section\n\t\t\tif ( div.querySelectorAll(\"[msallowcapture^='']\").length ) {\n\t\t\t\trbuggyQSA.push( \"[*^$]=\" + whitespace + \"*(?:''|\\\"\\\")\" );\n\t\t\t}\n\n\t\t\t// Support: IE8\n\t\t\t// Boolean attributes and \"value\" are not treated correctly\n\t\t\tif ( !div.querySelectorAll(\"[selected]\").length ) {\n\t\t\t\trbuggyQSA.push( \"\\\\[\" + whitespace + \"*(?:value|\" + booleans + \")\" );\n\t\t\t}\n\n\t\t\t// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+\n\t\t\tif ( !div.querySelectorAll( \"[id~=\" + expando + \"-]\" ).length ) {\n\t\t\t\trbuggyQSA.push(\"~=\");\n\t\t\t}\n\n\t\t\t// Webkit/Opera - :checked should return selected option elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( !div.querySelectorAll(\":checked\").length ) {\n\t\t\t\trbuggyQSA.push(\":checked\");\n\t\t\t}\n\n\t\t\t// Support: Safari 8+, iOS 8+\n\t\t\t// https://bugs.webkit.org/show_bug.cgi?id=136851\n\t\t\t// In-page `selector#id sibing-combinator selector` fails\n\t\t\tif ( !div.querySelectorAll( \"a#\" + expando + \"+*\" ).length ) {\n\t\t\t\trbuggyQSA.push(\".#.+[+~]\");\n\t\t\t}\n\t\t});\n\n\t\tassert(function( div ) {\n\t\t\t// Support: Windows 8 Native Apps\n\t\t\t// The type and name attributes are restricted during .innerHTML assignment\n\t\t\tvar input = document.createElement(\"input\");\n\t\t\tinput.setAttribute( \"type\", \"hidden\" );\n\t\t\tdiv.appendChild( input ).setAttribute( \"name\", \"D\" );\n\n\t\t\t// Support: IE8\n\t\t\t// Enforce case-sensitivity of name attribute\n\t\t\tif ( div.querySelectorAll(\"[name=d]\").length ) {\n\t\t\t\trbuggyQSA.push( \"name\" + whitespace + \"*[*^$|!~]?=\" );\n\t\t\t}\n\n\t\t\t// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( !div.querySelectorAll(\":enabled\").length ) {\n\t\t\t\trbuggyQSA.push( \":enabled\", \":disabled\" );\n\t\t\t}\n\n\t\t\t// Opera 10-11 does not throw on post-comma invalid pseudos\n\t\t\tdiv.querySelectorAll(\"*,:x\");\n\t\t\trbuggyQSA.push(\",.*:\");\n\t\t});\n\t}\n\n\tif ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||\n\t\tdocElem.webkitMatchesSelector ||\n\t\tdocElem.mozMatchesSelector ||\n\t\tdocElem.oMatchesSelector ||\n\t\tdocElem.msMatchesSelector) )) ) {\n\n\t\tassert(function( div ) {\n\t\t\t// Check to see if it's possible to do matchesSelector\n\t\t\t// on a disconnected node (IE 9)\n\t\t\tsupport.disconnectedMatch = matches.call( div, \"div\" );\n\n\t\t\t// This should fail with an exception\n\t\t\t// Gecko does not error, returns false instead\n\t\t\tmatches.call( div, \"[s!='']:x\" );\n\t\t\trbuggyMatches.push( \"!=\", pseudos );\n\t\t});\n\t}\n\n\trbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join(\"|\") );\n\trbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join(\"|\") );\n\n\t/* Contains\n\t---------------------------------------------------------------------- */\n\thasCompare = rnative.test( docElem.compareDocumentPosition );\n\n\t// Element contains another\n\t// Purposefully self-exclusive\n\t// As in, an element does not contain itself\n\tcontains = hasCompare || rnative.test( docElem.contains ) ?\n\t\tfunction( a, b ) {\n\t\t\tvar adown = a.nodeType === 9 ? a.documentElement : a,\n\t\t\t\tbup = b && b.parentNode;\n\t\t\treturn a === bup || !!( bup && bup.nodeType === 1 && (\n\t\t\t\tadown.contains ?\n\t\t\t\t\tadown.contains( bup ) :\n\t\t\t\t\ta.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16\n\t\t\t));\n\t\t} :\n\t\tfunction( a, b ) {\n\t\t\tif ( b ) {\n\t\t\t\twhile ( (b = b.parentNode) ) {\n\t\t\t\t\tif ( b === a ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t/* Sorting\n\t---------------------------------------------------------------------- */\n\n\t// Document order sorting\n\tsortOrder = hasCompare ?\n\tfunction( a, b ) {\n\n\t\t// Flag for duplicate removal\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\t// Sort on method existence if only one input has compareDocumentPosition\n\t\tvar compare = !a.compareDocumentPosition - !b.compareDocumentPosition;\n\t\tif ( compare ) {\n\t\t\treturn compare;\n\t\t}\n\n\t\t// Calculate position if both inputs belong to the same document\n\t\tcompare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?\n\t\t\ta.compareDocumentPosition( b ) :\n\n\t\t\t// Otherwise we know they are disconnected\n\t\t\t1;\n\n\t\t// Disconnected nodes\n\t\tif ( compare & 1 ||\n\t\t\t(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {\n\n\t\t\t// Choose the first element that is related to our preferred document\n\t\t\tif ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\t// Maintain original order\n\t\t\treturn sortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\t\t}\n\n\t\treturn compare & 4 ? -1 : 1;\n\t} :\n\tfunction( a, b ) {\n\t\t// Exit early if the nodes are identical\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\taup = a.parentNode,\n\t\t\tbup = b.parentNode,\n\t\t\tap = [ a ],\n\t\t\tbp = [ b ];\n\n\t\t// Parentless nodes are either documents or disconnected\n\t\tif ( !aup || !bup ) {\n\t\t\treturn a === document ? -1 :\n\t\t\t\tb === document ? 1 :\n\t\t\t\taup ? -1 :\n\t\t\t\tbup ? 1 :\n\t\t\t\tsortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\n\t\t// If the nodes are siblings, we can do a quick check\n\t\t} else if ( aup === bup ) {\n\t\t\treturn siblingCheck( a, b );\n\t\t}\n\n\t\t// Otherwise we need full lists of their ancestors for comparison\n\t\tcur = a;\n\t\twhile ( (cur = cur.parentNode) ) {\n\t\t\tap.unshift( cur );\n\t\t}\n\t\tcur = b;\n\t\twhile ( (cur = cur.parentNode) ) {\n\t\t\tbp.unshift( cur );\n\t\t}\n\n\t\t// Walk down the tree looking for a discrepancy\n\t\twhile ( ap[i] === bp[i] ) {\n\t\t\ti++;\n\t\t}\n\n\t\treturn i ?\n\t\t\t// Do a sibling check if the nodes have a common ancestor\n\t\t\tsiblingCheck( ap[i], bp[i] ) :\n\n\t\t\t// Otherwise nodes in our document sort first\n\t\t\tap[i] === preferredDoc ? -1 :\n\t\t\tbp[i] === preferredDoc ? 1 :\n\t\t\t0;\n\t};\n\n\treturn document;\n};\n\nSizzle.matches = function( expr, elements ) {\n\treturn Sizzle( expr, null, null, elements );\n};\n\nSizzle.matchesSelector = function( elem, expr ) {\n\t// Set document vars if needed\n\tif ( ( elem.ownerDocument || elem ) !== document ) {\n\t\tsetDocument( elem );\n\t}\n\n\t// Make sure that attribute selectors are quoted\n\texpr = expr.replace( rattributeQuotes, \"='$1']\" );\n\n\tif ( support.matchesSelector && documentIsHTML &&\n\t\t!compilerCache[ expr + \" \" ] &&\n\t\t( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&\n\t\t( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {\n\n\t\ttry {\n\t\t\tvar ret = matches.call( elem, expr );\n\n\t\t\t// IE 9's matchesSelector returns false on disconnected nodes\n\t\t\tif ( ret || support.disconnectedMatch ||\n\t\t\t\t\t// As well, disconnected nodes are said to be in a document\n\t\t\t\t\t// fragment in IE 9\n\t\t\t\t\telem.document && elem.document.nodeType !== 11 ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t} catch (e) {}\n\t}\n\n\treturn Sizzle( expr, document, null, [ elem ] ).length > 0;\n};\n\nSizzle.contains = function( context, elem ) {\n\t// Set document vars if needed\n\tif ( ( context.ownerDocument || context ) !== document ) {\n\t\tsetDocument( context );\n\t}\n\treturn contains( context, elem );\n};\n\nSizzle.attr = function( elem, name ) {\n\t// Set document vars if needed\n\tif ( ( elem.ownerDocument || elem ) !== document ) {\n\t\tsetDocument( elem );\n\t}\n\n\tvar fn = Expr.attrHandle[ name.toLowerCase() ],\n\t\t// Don't get fooled by Object.prototype properties (jQuery #13807)\n\t\tval = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?\n\t\t\tfn( elem, name, !documentIsHTML ) :\n\t\t\tundefined;\n\n\treturn val !== undefined ?\n\t\tval :\n\t\tsupport.attributes || !documentIsHTML ?\n\t\t\telem.getAttribute( name ) :\n\t\t\t(val = elem.getAttributeNode(name)) && val.specified ?\n\t\t\t\tval.value :\n\t\t\t\tnull;\n};\n\nSizzle.error = function( msg ) {\n\tthrow new Error( \"Syntax error, unrecognized expression: \" + msg );\n};\n\n/**\n * Document sorting and removing duplicates\n * @param {ArrayLike} results\n */\nSizzle.uniqueSort = function( results ) {\n\tvar elem,\n\t\tduplicates = [],\n\t\tj = 0,\n\t\ti = 0;\n\n\t// Unless we *know* we can detect duplicates, assume their presence\n\thasDuplicate = !support.detectDuplicates;\n\tsortInput = !support.sortStable && results.slice( 0 );\n\tresults.sort( sortOrder );\n\n\tif ( hasDuplicate ) {\n\t\twhile ( (elem = results[i++]) ) {\n\t\t\tif ( elem === results[ i ] ) {\n\t\t\t\tj = duplicates.push( i );\n\t\t\t}\n\t\t}\n\t\twhile ( j-- ) {\n\t\t\tresults.splice( duplicates[ j ], 1 );\n\t\t}\n\t}\n\n\t// Clear input after sorting to release objects\n\t// See https://github.com/jquery/sizzle/pull/225\n\tsortInput = null;\n\n\treturn results;\n};\n\n/**\n * Utility function for retrieving the text value of an array of DOM nodes\n * @param {Array|Element} elem\n */\ngetText = Sizzle.getText = function( elem ) {\n\tvar node,\n\t\tret = \"\",\n\t\ti = 0,\n\t\tnodeType = elem.nodeType;\n\n\tif ( !nodeType ) {\n\t\t// If no nodeType, this is expected to be an array\n\t\twhile ( (node = elem[i++]) ) {\n\t\t\t// Do not traverse comment nodes\n\t\t\tret += getText( node );\n\t\t}\n\t} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {\n\t\t// Use textContent for elements\n\t\t// innerText usage removed for consistency of new lines (jQuery #11153)\n\t\tif ( typeof elem.textContent === \"string\" ) {\n\t\t\treturn elem.textContent;\n\t\t} else {\n\t\t\t// Traverse its children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tret += getText( elem );\n\t\t\t}\n\t\t}\n\t} else if ( nodeType === 3 || nodeType === 4 ) {\n\t\treturn elem.nodeValue;\n\t}\n\t// Do not include comment or processing instruction nodes\n\n\treturn ret;\n};\n\nExpr = Sizzle.selectors = {\n\n\t// Can be adjusted by the user\n\tcacheLength: 50,\n\n\tcreatePseudo: markFunction,\n\n\tmatch: matchExpr,\n\n\tattrHandle: {},\n\n\tfind: {},\n\n\trelative: {\n\t\t\">\": { dir: \"parentNode\", first: true },\n\t\t\" \": { dir: \"parentNode\" },\n\t\t\"+\": { dir: \"previousSibling\", first: true },\n\t\t\"~\": { dir: \"previousSibling\" }\n\t},\n\n\tpreFilter: {\n\t\t\"ATTR\": function( match ) {\n\t\t\tmatch[1] = match[1].replace( runescape, funescape );\n\n\t\t\t// Move the given value to match[3] whether quoted or unquoted\n\t\t\tmatch[3] = ( match[3] || match[4] || match[5] || \"\" ).replace( runescape, funescape );\n\n\t\t\tif ( match[2] === \"~=\" ) {\n\t\t\t\tmatch[3] = \" \" + match[3] + \" \";\n\t\t\t}\n\n\t\t\treturn match.slice( 0, 4 );\n\t\t},\n\n\t\t\"CHILD\": function( match ) {\n\t\t\t/* matches from matchExpr[\"CHILD\"]\n\t\t\t\t1 type (only|nth|...)\n\t\t\t\t2 what (child|of-type)\n\t\t\t\t3 argument (even|odd|\\d*|\\d*n([+-]\\d+)?|...)\n\t\t\t\t4 xn-component of xn+y argument ([+-]?\\d*n|)\n\t\t\t\t5 sign of xn-component\n\t\t\t\t6 x of xn-component\n\t\t\t\t7 sign of y-component\n\t\t\t\t8 y of y-component\n\t\t\t*/\n\t\t\tmatch[1] = match[1].toLowerCase();\n\n\t\t\tif ( match[1].slice( 0, 3 ) === \"nth\" ) {\n\t\t\t\t// nth-* requires argument\n\t\t\t\tif ( !match[3] ) {\n\t\t\t\t\tSizzle.error( match[0] );\n\t\t\t\t}\n\n\t\t\t\t// numeric x and y parameters for Expr.filter.CHILD\n\t\t\t\t// remember that false/true cast respectively to 0/1\n\t\t\t\tmatch[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === \"even\" || match[3] === \"odd\" ) );\n\t\t\t\tmatch[5] = +( ( match[7] + match[8] ) || match[3] === \"odd\" );\n\n\t\t\t// other types prohibit arguments\n\t\t\t} else if ( match[3] ) {\n\t\t\t\tSizzle.error( match[0] );\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\n\t\t\"PSEUDO\": function( match ) {\n\t\t\tvar excess,\n\t\t\t\tunquoted = !match[6] && match[2];\n\n\t\t\tif ( matchExpr[\"CHILD\"].test( match[0] ) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Accept quoted arguments as-is\n\t\t\tif ( match[3] ) {\n\t\t\t\tmatch[2] = match[4] || match[5] || \"\";\n\n\t\t\t// Strip excess characters from unquoted arguments\n\t\t\t} else if ( unquoted && rpseudo.test( unquoted ) &&\n\t\t\t\t// Get excess from tokenize (recursively)\n\t\t\t\t(excess = tokenize( unquoted, true )) &&\n\t\t\t\t// advance to the next closing parenthesis\n\t\t\t\t(excess = unquoted.indexOf( \")\", unquoted.length - excess ) - unquoted.length) ) {\n\n\t\t\t\t// excess is a negative index\n\t\t\t\tmatch[0] = match[0].slice( 0, excess );\n\t\t\t\tmatch[2] = unquoted.slice( 0, excess );\n\t\t\t}\n\n\t\t\t// Return only captures needed by the pseudo filter method (type and argument)\n\t\t\treturn match.slice( 0, 3 );\n\t\t}\n\t},\n\n\tfilter: {\n\n\t\t\"TAG\": function( nodeNameSelector ) {\n\t\t\tvar nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn nodeNameSelector === \"*\" ?\n\t\t\t\tfunction() { return true; } :\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn elem.nodeName && elem.nodeName.toLowerCase() === nodeName;\n\t\t\t\t};\n\t\t},\n\n\t\t\"CLASS\": function( className ) {\n\t\t\tvar pattern = classCache[ className + \" \" ];\n\n\t\t\treturn pattern ||\n\t\t\t\t(pattern = new RegExp( \"(^|\" + whitespace + \")\" + className + \"(\" + whitespace + \"|$)\" )) &&\n\t\t\t\tclassCache( className, function( elem ) {\n\t\t\t\t\treturn pattern.test( typeof elem.className === \"string\" && elem.className || typeof elem.getAttribute !== \"undefined\" && elem.getAttribute(\"class\") || \"\" );\n\t\t\t\t});\n\t\t},\n\n\t\t\"ATTR\": function( name, operator, check ) {\n\t\t\treturn function( elem ) {\n\t\t\t\tvar result = Sizzle.attr( elem, name );\n\n\t\t\t\tif ( result == null ) {\n\t\t\t\t\treturn operator === \"!=\";\n\t\t\t\t}\n\t\t\t\tif ( !operator ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tresult += \"\";\n\n\t\t\t\treturn operator === \"=\" ? result === check :\n\t\t\t\t\toperator === \"!=\" ? result !== check :\n\t\t\t\t\toperator === \"^=\" ? check && result.indexOf( check ) === 0 :\n\t\t\t\t\toperator === \"*=\" ? check && result.indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"$=\" ? check && result.slice( -check.length ) === check :\n\t\t\t\t\toperator === \"~=\" ? ( \" \" + result.replace( rwhitespace, \" \" ) + \" \" ).indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"|=\" ? result === check || result.slice( 0, check.length + 1 ) === check + \"-\" :\n\t\t\t\t\tfalse;\n\t\t\t};\n\t\t},\n\n\t\t\"CHILD\": function( type, what, argument, first, last ) {\n\t\t\tvar simple = type.slice( 0, 3 ) !== \"nth\",\n\t\t\t\tforward = type.slice( -4 ) !== \"last\",\n\t\t\t\tofType = what === \"of-type\";\n\n\t\t\treturn first === 1 && last === 0 ?\n\n\t\t\t\t// Shortcut for :nth-*(n)\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn !!elem.parentNode;\n\t\t\t\t} :\n\n\t\t\t\tfunction( elem, context, xml ) {\n\t\t\t\t\tvar cache, uniqueCache, outerCache, node, nodeIndex, start,\n\t\t\t\t\t\tdir = simple !== forward ? \"nextSibling\" : \"previousSibling\",\n\t\t\t\t\t\tparent = elem.parentNode,\n\t\t\t\t\t\tname = ofType && elem.nodeName.toLowerCase(),\n\t\t\t\t\t\tuseCache = !xml && !ofType,\n\t\t\t\t\t\tdiff = false;\n\n\t\t\t\t\tif ( parent ) {\n\n\t\t\t\t\t\t// :(first|last|only)-(child|of-type)\n\t\t\t\t\t\tif ( simple ) {\n\t\t\t\t\t\t\twhile ( dir ) {\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\twhile ( (node = node[ dir ]) ) {\n\t\t\t\t\t\t\t\t\tif ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) {\n\n\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Reverse direction for :only-* (if we haven't yet done so)\n\t\t\t\t\t\t\t\tstart = dir = type === \"only\" && !start && \"nextSibling\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstart = [ forward ? parent.firstChild : parent.lastChild ];\n\n\t\t\t\t\t\t// non-xml :nth-child(...) stores cache data on `parent`\n\t\t\t\t\t\tif ( forward && useCache ) {\n\n\t\t\t\t\t\t\t// Seek `elem` from a previously-cached index\n\n\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\tnode = parent;\n\t\t\t\t\t\t\touterCache = node[ expando ] || (node[ expando ] = {});\n\n\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t(outerCache[ node.uniqueID ] = {});\n\n\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\tdiff = nodeIndex && cache[ 2 ];\n\t\t\t\t\t\t\tnode = nodeIndex && parent.childNodes[ nodeIndex ];\n\n\t\t\t\t\t\t\twhile ( (node = ++nodeIndex && node && node[ dir ] ||\n\n\t\t\t\t\t\t\t\t// Fallback to seeking `elem` from the start\n\t\t\t\t\t\t\t\t(diff = nodeIndex = 0) || start.pop()) ) {\n\n\t\t\t\t\t\t\t\t// When found, cache indexes on `parent` and break\n\t\t\t\t\t\t\t\tif ( node.nodeType === 1 && ++diff && node === elem ) {\n\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, nodeIndex, diff ];\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Use previously-cached element index if available\n\t\t\t\t\t\t\tif ( useCache ) {\n\t\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\touterCache = node[ expando ] || (node[ expando ] = {});\n\n\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t(outerCache[ node.uniqueID ] = {});\n\n\t\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\t\tdiff = nodeIndex;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// xml :nth-child(...)\n\t\t\t\t\t\t\t// or :nth-last-child(...) or :nth(-last)?-of-type(...)\n\t\t\t\t\t\t\tif ( diff === false ) {\n\t\t\t\t\t\t\t\t// Use the same loop as above to seek `elem` from the start\n\t\t\t\t\t\t\t\twhile ( (node = ++nodeIndex && node && node[ dir ] ||\n\t\t\t\t\t\t\t\t\t(diff = nodeIndex = 0) || start.pop()) ) {\n\n\t\t\t\t\t\t\t\t\tif ( ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) &&\n\t\t\t\t\t\t\t\t\t\t++diff ) {\n\n\t\t\t\t\t\t\t\t\t\t// Cache the index of each encountered element\n\t\t\t\t\t\t\t\t\t\tif ( useCache ) {\n\t\t\t\t\t\t\t\t\t\t\touterCache = node[ expando ] || (node[ expando ] = {});\n\n\t\t\t\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t\t\t\t(outerCache[ node.uniqueID ] = {});\n\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, diff ];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tif ( node === elem ) {\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Incorporate the offset, then check against cycle size\n\t\t\t\t\t\tdiff -= last;\n\t\t\t\t\t\treturn diff === first || ( diff % first === 0 && diff / first >= 0 );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t},\n\n\t\t\"PSEUDO\": function( pseudo, argument ) {\n\t\t\t// pseudo-class names are case-insensitive\n\t\t\t// http://www.w3.org/TR/selectors/#pseudo-classes\n\t\t\t// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters\n\t\t\t// Remember that setFilters inherits from pseudos\n\t\t\tvar args,\n\t\t\t\tfn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||\n\t\t\t\t\tSizzle.error( \"unsupported pseudo: \" + pseudo );\n\n\t\t\t// The user may use createPseudo to indicate that\n\t\t\t// arguments are needed to create the filter function\n\t\t\t// just as Sizzle does\n\t\t\tif ( fn[ expando ] ) {\n\t\t\t\treturn fn( argument );\n\t\t\t}\n\n\t\t\t// But maintain support for old signatures\n\t\t\tif ( fn.length > 1 ) {\n\t\t\t\targs = [ pseudo, pseudo, \"\", argument ];\n\t\t\t\treturn Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?\n\t\t\t\t\tmarkFunction(function( seed, matches ) {\n\t\t\t\t\t\tvar idx,\n\t\t\t\t\t\t\tmatched = fn( seed, argument ),\n\t\t\t\t\t\t\ti = matched.length;\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tidx = indexOf( seed, matched[i] );\n\t\t\t\t\t\t\tseed[ idx ] = !( matches[ idx ] = matched[i] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}) :\n\t\t\t\t\tfunction( elem ) {\n\t\t\t\t\t\treturn fn( elem, 0, args );\n\t\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn fn;\n\t\t}\n\t},\n\n\tpseudos: {\n\t\t// Potentially complex pseudos\n\t\t\"not\": markFunction(function( selector ) {\n\t\t\t// Trim the selector passed to compile\n\t\t\t// to avoid treating leading and trailing\n\t\t\t// spaces as combinators\n\t\t\tvar input = [],\n\t\t\t\tresults = [],\n\t\t\t\tmatcher = compile( selector.replace( rtrim, \"$1\" ) );\n\n\t\t\treturn matcher[ expando ] ?\n\t\t\t\tmarkFunction(function( seed, matches, context, xml ) {\n\t\t\t\t\tvar elem,\n\t\t\t\t\t\tunmatched = matcher( seed, null, xml, [] ),\n\t\t\t\t\t\ti = seed.length;\n\n\t\t\t\t\t// Match elements unmatched by `matcher`\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( (elem = unmatched[i]) ) {\n\t\t\t\t\t\t\tseed[i] = !(matches[i] = elem);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}) :\n\t\t\t\tfunction( elem, context, xml ) {\n\t\t\t\t\tinput[0] = elem;\n\t\t\t\t\tmatcher( input, null, xml, results );\n\t\t\t\t\t// Don't keep the element (issue #299)\n\t\t\t\t\tinput[0] = null;\n\t\t\t\t\treturn !results.pop();\n\t\t\t\t};\n\t\t}),\n\n\t\t\"has\": markFunction(function( selector ) {\n\t\t\treturn function( elem ) {\n\t\t\t\treturn Sizzle( selector, elem ).length > 0;\n\t\t\t};\n\t\t}),\n\n\t\t\"contains\": markFunction(function( text ) {\n\t\t\ttext = text.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;\n\t\t\t};\n\t\t}),\n\n\t\t// \"Whether an element is represented by a :lang() selector\n\t\t// is based solely on the element's language value\n\t\t// being equal to the identifier C,\n\t\t// or beginning with the identifier C immediately followed by \"-\".\n\t\t// The matching of C against the element's language value is performed case-insensitively.\n\t\t// The identifier C does not have to be a valid language name.\"\n\t\t// http://www.w3.org/TR/selectors/#lang-pseudo\n\t\t\"lang\": markFunction( function( lang ) {\n\t\t\t// lang value must be a valid identifier\n\t\t\tif ( !ridentifier.test(lang || \"\") ) {\n\t\t\t\tSizzle.error( \"unsupported lang: \" + lang );\n\t\t\t}\n\t\t\tlang = lang.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn function( elem ) {\n\t\t\t\tvar elemLang;\n\t\t\t\tdo {\n\t\t\t\t\tif ( (elemLang = documentIsHTML ?\n\t\t\t\t\t\telem.lang :\n\t\t\t\t\t\telem.getAttribute(\"xml:lang\") || elem.getAttribute(\"lang\")) ) {\n\n\t\t\t\t\t\telemLang = elemLang.toLowerCase();\n\t\t\t\t\t\treturn elemLang === lang || elemLang.indexOf( lang + \"-\" ) === 0;\n\t\t\t\t\t}\n\t\t\t\t} while ( (elem = elem.parentNode) && elem.nodeType === 1 );\n\t\t\t\treturn false;\n\t\t\t};\n\t\t}),\n\n\t\t// Miscellaneous\n\t\t\"target\": function( elem ) {\n\t\t\tvar hash = window.location && window.location.hash;\n\t\t\treturn hash && hash.slice( 1 ) === elem.id;\n\t\t},\n\n\t\t\"root\": function( elem ) {\n\t\t\treturn elem === docElem;\n\t\t},\n\n\t\t\"focus\": function( elem ) {\n\t\t\treturn elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);\n\t\t},\n\n\t\t// Boolean properties\n\t\t\"enabled\": function( elem ) {\n\t\t\treturn elem.disabled === false;\n\t\t},\n\n\t\t\"disabled\": function( elem ) {\n\t\t\treturn elem.disabled === true;\n\t\t},\n\n\t\t\"checked\": function( elem ) {\n\t\t\t// In CSS3, :checked should return both checked and selected elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\tvar nodeName = elem.nodeName.toLowerCase();\n\t\t\treturn (nodeName === \"input\" && !!elem.checked) || (nodeName === \"option\" && !!elem.selected);\n\t\t},\n\n\t\t\"selected\": function( elem ) {\n\t\t\t// Accessing this property makes selected-by-default\n\t\t\t// options in Safari work properly\n\t\t\tif ( elem.parentNode ) {\n\t\t\t\telem.parentNode.selectedIndex;\n\t\t\t}\n\n\t\t\treturn elem.selected === true;\n\t\t},\n\n\t\t// Contents\n\t\t\"empty\": function( elem ) {\n\t\t\t// http://www.w3.org/TR/selectors/#empty-pseudo\n\t\t\t// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),\n\t\t\t//   but not by others (comment: 8; processing instruction: 7; etc.)\n\t\t\t// nodeType < 6 works because attributes (2) do not appear as children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tif ( elem.nodeType < 6 ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\t\"parent\": function( elem ) {\n\t\t\treturn !Expr.pseudos[\"empty\"]( elem );\n\t\t},\n\n\t\t// Element/input types\n\t\t\"header\": function( elem ) {\n\t\t\treturn rheader.test( elem.nodeName );\n\t\t},\n\n\t\t\"input\": function( elem ) {\n\t\t\treturn rinputs.test( elem.nodeName );\n\t\t},\n\n\t\t\"button\": function( elem ) {\n\t\t\tvar name = elem.nodeName.toLowerCase();\n\t\t\treturn name === \"input\" && elem.type === \"button\" || name === \"button\";\n\t\t},\n\n\t\t\"text\": function( elem ) {\n\t\t\tvar attr;\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" &&\n\t\t\t\telem.type === \"text\" &&\n\n\t\t\t\t// Support: IE<8\n\t\t\t\t// New HTML5 attribute values (e.g., \"search\") appear with elem.type === \"text\"\n\t\t\t\t( (attr = elem.getAttribute(\"type\")) == null || attr.toLowerCase() === \"text\" );\n\t\t},\n\n\t\t// Position-in-collection\n\t\t\"first\": createPositionalPseudo(function() {\n\t\t\treturn [ 0 ];\n\t\t}),\n\n\t\t\"last\": createPositionalPseudo(function( matchIndexes, length ) {\n\t\t\treturn [ length - 1 ];\n\t\t}),\n\n\t\t\"eq\": createPositionalPseudo(function( matchIndexes, length, argument ) {\n\t\t\treturn [ argument < 0 ? argument + length : argument ];\n\t\t}),\n\n\t\t\"even\": createPositionalPseudo(function( matchIndexes, length ) {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t}),\n\n\t\t\"odd\": createPositionalPseudo(function( matchIndexes, length ) {\n\t\t\tvar i = 1;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t}),\n\n\t\t\"lt\": createPositionalPseudo(function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ? argument + length : argument;\n\t\t\tfor ( ; --i >= 0; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t}),\n\n\t\t\"gt\": createPositionalPseudo(function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ? argument + length : argument;\n\t\t\tfor ( ; ++i < length; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t})\n\t}\n};\n\nExpr.pseudos[\"nth\"] = Expr.pseudos[\"eq\"];\n\n// Add button/input type pseudos\nfor ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {\n\tExpr.pseudos[ i ] = createInputPseudo( i );\n}\nfor ( i in { submit: true, reset: true } ) {\n\tExpr.pseudos[ i ] = createButtonPseudo( i );\n}\n\n// Easy API for creating new setFilters\nfunction setFilters() {}\nsetFilters.prototype = Expr.filters = Expr.pseudos;\nExpr.setFilters = new setFilters();\n\ntokenize = Sizzle.tokenize = function( selector, parseOnly ) {\n\tvar matched, match, tokens, type,\n\t\tsoFar, groups, preFilters,\n\t\tcached = tokenCache[ selector + \" \" ];\n\n\tif ( cached ) {\n\t\treturn parseOnly ? 0 : cached.slice( 0 );\n\t}\n\n\tsoFar = selector;\n\tgroups = [];\n\tpreFilters = Expr.preFilter;\n\n\twhile ( soFar ) {\n\n\t\t// Comma and first run\n\t\tif ( !matched || (match = rcomma.exec( soFar )) ) {\n\t\t\tif ( match ) {\n\t\t\t\t// Don't consume trailing commas as valid\n\t\t\t\tsoFar = soFar.slice( match[0].length ) || soFar;\n\t\t\t}\n\t\t\tgroups.push( (tokens = []) );\n\t\t}\n\n\t\tmatched = false;\n\n\t\t// Combinators\n\t\tif ( (match = rcombinators.exec( soFar )) ) {\n\t\t\tmatched = match.shift();\n\t\t\ttokens.push({\n\t\t\t\tvalue: matched,\n\t\t\t\t// Cast descendant combinators to space\n\t\t\t\ttype: match[0].replace( rtrim, \" \" )\n\t\t\t});\n\t\t\tsoFar = soFar.slice( matched.length );\n\t\t}\n\n\t\t// Filters\n\t\tfor ( type in Expr.filter ) {\n\t\t\tif ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||\n\t\t\t\t(match = preFilters[ type ]( match ))) ) {\n\t\t\t\tmatched = match.shift();\n\t\t\t\ttokens.push({\n\t\t\t\t\tvalue: matched,\n\t\t\t\t\ttype: type,\n\t\t\t\t\tmatches: match\n\t\t\t\t});\n\t\t\t\tsoFar = soFar.slice( matched.length );\n\t\t\t}\n\t\t}\n\n\t\tif ( !matched ) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Return the length of the invalid excess\n\t// if we're just parsing\n\t// Otherwise, throw an error or return tokens\n\treturn parseOnly ?\n\t\tsoFar.length :\n\t\tsoFar ?\n\t\t\tSizzle.error( selector ) :\n\t\t\t// Cache the tokens\n\t\t\ttokenCache( selector, groups ).slice( 0 );\n};\n\nfunction toSelector( tokens ) {\n\tvar i = 0,\n\t\tlen = tokens.length,\n\t\tselector = \"\";\n\tfor ( ; i < len; i++ ) {\n\t\tselector += tokens[i].value;\n\t}\n\treturn selector;\n}\n\nfunction addCombinator( matcher, combinator, base ) {\n\tvar dir = combinator.dir,\n\t\tcheckNonElements = base && dir === \"parentNode\",\n\t\tdoneName = done++;\n\n\treturn combinator.first ?\n\t\t// Check against closest ancestor/preceding element\n\t\tfunction( elem, context, xml ) {\n\t\t\twhile ( (elem = elem[ dir ]) ) {\n\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\treturn matcher( elem, context, xml );\n\t\t\t\t}\n\t\t\t}\n\t\t} :\n\n\t\t// Check against all ancestor/preceding elements\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar oldCache, uniqueCache, outerCache,\n\t\t\t\tnewCache = [ dirruns, doneName ];\n\n\t\t\t// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching\n\t\t\tif ( xml ) {\n\t\t\t\twhile ( (elem = elem[ dir ]) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\tif ( matcher( elem, context, xml ) ) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twhile ( (elem = elem[ dir ]) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\touterCache = elem[ expando ] || (elem[ expando ] = {});\n\n\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\tuniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});\n\n\t\t\t\t\t\tif ( (oldCache = uniqueCache[ dir ]) &&\n\t\t\t\t\t\t\toldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {\n\n\t\t\t\t\t\t\t// Assign to newCache so results back-propagate to previous elements\n\t\t\t\t\t\t\treturn (newCache[ 2 ] = oldCache[ 2 ]);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Reuse newcache so results back-propagate to previous elements\n\t\t\t\t\t\t\tuniqueCache[ dir ] = newCache;\n\n\t\t\t\t\t\t\t// A match means we're done; a fail means we have to keep checking\n\t\t\t\t\t\t\tif ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n}\n\nfunction elementMatcher( matchers ) {\n\treturn matchers.length > 1 ?\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar i = matchers.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( !matchers[i]( elem, context, xml ) ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} :\n\t\tmatchers[0];\n}\n\nfunction multipleContexts( selector, contexts, results ) {\n\tvar i = 0,\n\t\tlen = contexts.length;\n\tfor ( ; i < len; i++ ) {\n\t\tSizzle( selector, contexts[i], results );\n\t}\n\treturn results;\n}\n\nfunction condense( unmatched, map, filter, context, xml ) {\n\tvar elem,\n\t\tnewUnmatched = [],\n\t\ti = 0,\n\t\tlen = unmatched.length,\n\t\tmapped = map != null;\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( (elem = unmatched[i]) ) {\n\t\t\tif ( !filter || filter( elem, context, xml ) ) {\n\t\t\t\tnewUnmatched.push( elem );\n\t\t\t\tif ( mapped ) {\n\t\t\t\t\tmap.push( i );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn newUnmatched;\n}\n\nfunction setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {\n\tif ( postFilter && !postFilter[ expando ] ) {\n\t\tpostFilter = setMatcher( postFilter );\n\t}\n\tif ( postFinder && !postFinder[ expando ] ) {\n\t\tpostFinder = setMatcher( postFinder, postSelector );\n\t}\n\treturn markFunction(function( seed, results, context, xml ) {\n\t\tvar temp, i, elem,\n\t\t\tpreMap = [],\n\t\t\tpostMap = [],\n\t\t\tpreexisting = results.length,\n\n\t\t\t// Get initial elements from seed or context\n\t\t\telems = seed || multipleContexts( selector || \"*\", context.nodeType ? [ context ] : context, [] ),\n\n\t\t\t// Prefilter to get matcher input, preserving a map for seed-results synchronization\n\t\t\tmatcherIn = preFilter && ( seed || !selector ) ?\n\t\t\t\tcondense( elems, preMap, preFilter, context, xml ) :\n\t\t\t\telems,\n\n\t\t\tmatcherOut = matcher ?\n\t\t\t\t// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,\n\t\t\t\tpostFinder || ( seed ? preFilter : preexisting || postFilter ) ?\n\n\t\t\t\t\t// ...intermediate processing is necessary\n\t\t\t\t\t[] :\n\n\t\t\t\t\t// ...otherwise use results directly\n\t\t\t\t\tresults :\n\t\t\t\tmatcherIn;\n\n\t\t// Find primary matches\n\t\tif ( matcher ) {\n\t\t\tmatcher( matcherIn, matcherOut, context, xml );\n\t\t}\n\n\t\t// Apply postFilter\n\t\tif ( postFilter ) {\n\t\t\ttemp = condense( matcherOut, postMap );\n\t\t\tpostFilter( temp, [], context, xml );\n\n\t\t\t// Un-match failing elements by moving them back to matcherIn\n\t\t\ti = temp.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( (elem = temp[i]) ) {\n\t\t\t\t\tmatcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( seed ) {\n\t\t\tif ( postFinder || preFilter ) {\n\t\t\t\tif ( postFinder ) {\n\t\t\t\t\t// Get the final matcherOut by condensing this intermediate into postFinder contexts\n\t\t\t\t\ttemp = [];\n\t\t\t\t\ti = matcherOut.length;\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( (elem = matcherOut[i]) ) {\n\t\t\t\t\t\t\t// Restore matcherIn since elem is not yet a final match\n\t\t\t\t\t\t\ttemp.push( (matcherIn[i] = elem) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpostFinder( null, (matcherOut = []), temp, xml );\n\t\t\t\t}\n\n\t\t\t\t// Move matched elements from seed to results to keep them synchronized\n\t\t\t\ti = matcherOut.length;\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\tif ( (elem = matcherOut[i]) &&\n\t\t\t\t\t\t(temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {\n\n\t\t\t\t\t\tseed[temp] = !(results[temp] = elem);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Add elements to results, through postFinder if defined\n\t\t} else {\n\t\t\tmatcherOut = condense(\n\t\t\t\tmatcherOut === results ?\n\t\t\t\t\tmatcherOut.splice( preexisting, matcherOut.length ) :\n\t\t\t\t\tmatcherOut\n\t\t\t);\n\t\t\tif ( postFinder ) {\n\t\t\t\tpostFinder( null, results, matcherOut, xml );\n\t\t\t} else {\n\t\t\t\tpush.apply( results, matcherOut );\n\t\t\t}\n\t\t}\n\t});\n}\n\nfunction matcherFromTokens( tokens ) {\n\tvar checkContext, matcher, j,\n\t\tlen = tokens.length,\n\t\tleadingRelative = Expr.relative[ tokens[0].type ],\n\t\timplicitRelative = leadingRelative || Expr.relative[\" \"],\n\t\ti = leadingRelative ? 1 : 0,\n\n\t\t// The foundational matcher ensures that elements are reachable from top-level context(s)\n\t\tmatchContext = addCombinator( function( elem ) {\n\t\t\treturn elem === checkContext;\n\t\t}, implicitRelative, true ),\n\t\tmatchAnyContext = addCombinator( function( elem ) {\n\t\t\treturn indexOf( checkContext, elem ) > -1;\n\t\t}, implicitRelative, true ),\n\t\tmatchers = [ function( elem, context, xml ) {\n\t\t\tvar ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (\n\t\t\t\t(checkContext = context).nodeType ?\n\t\t\t\t\tmatchContext( elem, context, xml ) :\n\t\t\t\t\tmatchAnyContext( elem, context, xml ) );\n\t\t\t// Avoid hanging onto element (issue #299)\n\t\t\tcheckContext = null;\n\t\t\treturn ret;\n\t\t} ];\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( (matcher = Expr.relative[ tokens[i].type ]) ) {\n\t\t\tmatchers = [ addCombinator(elementMatcher( matchers ), matcher) ];\n\t\t} else {\n\t\t\tmatcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );\n\n\t\t\t// Return special upon seeing a positional matcher\n\t\t\tif ( matcher[ expando ] ) {\n\t\t\t\t// Find the next relative operator (if any) for proper handling\n\t\t\t\tj = ++i;\n\t\t\t\tfor ( ; j < len; j++ ) {\n\t\t\t\t\tif ( Expr.relative[ tokens[j].type ] ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn setMatcher(\n\t\t\t\t\ti > 1 && elementMatcher( matchers ),\n\t\t\t\t\ti > 1 && toSelector(\n\t\t\t\t\t\t// If the preceding token was a descendant combinator, insert an implicit any-element `*`\n\t\t\t\t\t\ttokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === \" \" ? \"*\" : \"\" })\n\t\t\t\t\t).replace( rtrim, \"$1\" ),\n\t\t\t\t\tmatcher,\n\t\t\t\t\ti < j && matcherFromTokens( tokens.slice( i, j ) ),\n\t\t\t\t\tj < len && matcherFromTokens( (tokens = tokens.slice( j )) ),\n\t\t\t\t\tj < len && toSelector( tokens )\n\t\t\t\t);\n\t\t\t}\n\t\t\tmatchers.push( matcher );\n\t\t}\n\t}\n\n\treturn elementMatcher( matchers );\n}\n\nfunction matcherFromGroupMatchers( elementMatchers, setMatchers ) {\n\tvar bySet = setMatchers.length > 0,\n\t\tbyElement = elementMatchers.length > 0,\n\t\tsuperMatcher = function( seed, context, xml, results, outermost ) {\n\t\t\tvar elem, j, matcher,\n\t\t\t\tmatchedCount = 0,\n\t\t\t\ti = \"0\",\n\t\t\t\tunmatched = seed && [],\n\t\t\t\tsetMatched = [],\n\t\t\t\tcontextBackup = outermostContext,\n\t\t\t\t// We must always have either seed elements or outermost context\n\t\t\t\telems = seed || byElement && Expr.find[\"TAG\"]( \"*\", outermost ),\n\t\t\t\t// Use integer dirruns iff this is the outermost matcher\n\t\t\t\tdirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),\n\t\t\t\tlen = elems.length;\n\n\t\t\tif ( outermost ) {\n\t\t\t\toutermostContext = context === document || context || outermost;\n\t\t\t}\n\n\t\t\t// Add elements passing elementMatchers directly to results\n\t\t\t// Support: IE<9, Safari\n\t\t\t// Tolerate NodeList properties (IE: \"length\"; Safari: <number>) matching elements by id\n\t\t\tfor ( ; i !== len && (elem = elems[i]) != null; i++ ) {\n\t\t\t\tif ( byElement && elem ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\tif ( !context && elem.ownerDocument !== document ) {\n\t\t\t\t\t\tsetDocument( elem );\n\t\t\t\t\t\txml = !documentIsHTML;\n\t\t\t\t\t}\n\t\t\t\t\twhile ( (matcher = elementMatchers[j++]) ) {\n\t\t\t\t\t\tif ( matcher( elem, context || document, xml) ) {\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( outermost ) {\n\t\t\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Track unmatched elements for set filters\n\t\t\t\tif ( bySet ) {\n\t\t\t\t\t// They will have gone through all possible matchers\n\t\t\t\t\tif ( (elem = !matcher && elem) ) {\n\t\t\t\t\t\tmatchedCount--;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Lengthen the array for every element, matched or not\n\t\t\t\t\tif ( seed ) {\n\t\t\t\t\t\tunmatched.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// `i` is now the count of elements visited above, and adding it to `matchedCount`\n\t\t\t// makes the latter nonnegative.\n\t\t\tmatchedCount += i;\n\n\t\t\t// Apply set filters to unmatched elements\n\t\t\t// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`\n\t\t\t// equals `i`), unless we didn't visit _any_ elements in the above loop because we have\n\t\t\t// no element matchers and no seed.\n\t\t\t// Incrementing an initially-string \"0\" `i` allows `i` to remain a string only in that\n\t\t\t// case, which will result in a \"00\" `matchedCount` that differs from `i` but is also\n\t\t\t// numerically zero.\n\t\t\tif ( bySet && i !== matchedCount ) {\n\t\t\t\tj = 0;\n\t\t\t\twhile ( (matcher = setMatchers[j++]) ) {\n\t\t\t\t\tmatcher( unmatched, setMatched, context, xml );\n\t\t\t\t}\n\n\t\t\t\tif ( seed ) {\n\t\t\t\t\t// Reintegrate element matches to eliminate the need for sorting\n\t\t\t\t\tif ( matchedCount > 0 ) {\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tif ( !(unmatched[i] || setMatched[i]) ) {\n\t\t\t\t\t\t\t\tsetMatched[i] = pop.call( results );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Discard index placeholder values to get only actual matches\n\t\t\t\t\tsetMatched = condense( setMatched );\n\t\t\t\t}\n\n\t\t\t\t// Add matches to results\n\t\t\t\tpush.apply( results, setMatched );\n\n\t\t\t\t// Seedless set matches succeeding multiple successful matchers stipulate sorting\n\t\t\t\tif ( outermost && !seed && setMatched.length > 0 &&\n\t\t\t\t\t( matchedCount + setMatchers.length ) > 1 ) {\n\n\t\t\t\t\tSizzle.uniqueSort( results );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Override manipulation of globals by nested matchers\n\t\t\tif ( outermost ) {\n\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\toutermostContext = contextBackup;\n\t\t\t}\n\n\t\t\treturn unmatched;\n\t\t};\n\n\treturn bySet ?\n\t\tmarkFunction( superMatcher ) :\n\t\tsuperMatcher;\n}\n\ncompile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {\n\tvar i,\n\t\tsetMatchers = [],\n\t\telementMatchers = [],\n\t\tcached = compilerCache[ selector + \" \" ];\n\n\tif ( !cached ) {\n\t\t// Generate a function of recursive functions that can be used to check each element\n\t\tif ( !match ) {\n\t\t\tmatch = tokenize( selector );\n\t\t}\n\t\ti = match.length;\n\t\twhile ( i-- ) {\n\t\t\tcached = matcherFromTokens( match[i] );\n\t\t\tif ( cached[ expando ] ) {\n\t\t\t\tsetMatchers.push( cached );\n\t\t\t} else {\n\t\t\t\telementMatchers.push( cached );\n\t\t\t}\n\t\t}\n\n\t\t// Cache the compiled function\n\t\tcached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );\n\n\t\t// Save selector and tokenization\n\t\tcached.selector = selector;\n\t}\n\treturn cached;\n};\n\n/**\n * A low-level selection function that works with Sizzle's compiled\n *  selector functions\n * @param {String|Function} selector A selector or a pre-compiled\n *  selector function built with Sizzle.compile\n * @param {Element} context\n * @param {Array} [results]\n * @param {Array} [seed] A set of elements to match against\n */\nselect = Sizzle.select = function( selector, context, results, seed ) {\n\tvar i, tokens, token, type, find,\n\t\tcompiled = typeof selector === \"function\" && selector,\n\t\tmatch = !seed && tokenize( (selector = compiled.selector || selector) );\n\n\tresults = results || [];\n\n\t// Try to minimize operations if there is only one selector in the list and no seed\n\t// (the latter of which guarantees us context)\n\tif ( match.length === 1 ) {\n\n\t\t// Reduce context if the leading compound selector is an ID\n\t\ttokens = match[0] = match[0].slice( 0 );\n\t\tif ( tokens.length > 2 && (token = tokens[0]).type === \"ID\" &&\n\t\t\t\tsupport.getById && context.nodeType === 9 && documentIsHTML &&\n\t\t\t\tExpr.relative[ tokens[1].type ] ) {\n\n\t\t\tcontext = ( Expr.find[\"ID\"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];\n\t\t\tif ( !context ) {\n\t\t\t\treturn results;\n\n\t\t\t// Precompiled matchers will still verify ancestry, so step up a level\n\t\t\t} else if ( compiled ) {\n\t\t\t\tcontext = context.parentNode;\n\t\t\t}\n\n\t\t\tselector = selector.slice( tokens.shift().value.length );\n\t\t}\n\n\t\t// Fetch a seed set for right-to-left matching\n\t\ti = matchExpr[\"needsContext\"].test( selector ) ? 0 : tokens.length;\n\t\twhile ( i-- ) {\n\t\t\ttoken = tokens[i];\n\n\t\t\t// Abort if we hit a combinator\n\t\t\tif ( Expr.relative[ (type = token.type) ] ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( (find = Expr.find[ type ]) ) {\n\t\t\t\t// Search, expanding context for leading sibling combinators\n\t\t\t\tif ( (seed = find(\n\t\t\t\t\ttoken.matches[0].replace( runescape, funescape ),\n\t\t\t\t\trsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context\n\t\t\t\t)) ) {\n\n\t\t\t\t\t// If seed is empty or no tokens remain, we can return early\n\t\t\t\t\ttokens.splice( i, 1 );\n\t\t\t\t\tselector = seed.length && toSelector( tokens );\n\t\t\t\t\tif ( !selector ) {\n\t\t\t\t\t\tpush.apply( results, seed );\n\t\t\t\t\t\treturn results;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Compile and execute a filtering function if one is not provided\n\t// Provide `match` to avoid retokenization if we modified the selector above\n\t( compiled || compile( selector, match ) )(\n\t\tseed,\n\t\tcontext,\n\t\t!documentIsHTML,\n\t\tresults,\n\t\t!context || rsibling.test( selector ) && testContext( context.parentNode ) || context\n\t);\n\treturn results;\n};\n\n// One-time assignments\n\n// Sort stability\nsupport.sortStable = expando.split(\"\").sort( sortOrder ).join(\"\") === expando;\n\n// Support: Chrome 14-35+\n// Always assume duplicates if they aren't passed to the comparison function\nsupport.detectDuplicates = !!hasDuplicate;\n\n// Initialize against the default document\nsetDocument();\n\n// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)\n// Detached nodes confoundingly follow *each other*\nsupport.sortDetached = assert(function( div1 ) {\n\t// Should return 1, but returns 4 (following)\n\treturn div1.compareDocumentPosition( document.createElement(\"div\") ) & 1;\n});\n\n// Support: IE<8\n// Prevent attribute/property \"interpolation\"\n// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx\nif ( !assert(function( div ) {\n\tdiv.innerHTML = \"<a href='#'></a>\";\n\treturn div.firstChild.getAttribute(\"href\") === \"#\" ;\n}) ) {\n\taddHandle( \"type|href|height|width\", function( elem, name, isXML ) {\n\t\tif ( !isXML ) {\n\t\t\treturn elem.getAttribute( name, name.toLowerCase() === \"type\" ? 1 : 2 );\n\t\t}\n\t});\n}\n\n// Support: IE<9\n// Use defaultValue in place of getAttribute(\"value\")\nif ( !support.attributes || !assert(function( div ) {\n\tdiv.innerHTML = \"<input/>\";\n\tdiv.firstChild.setAttribute( \"value\", \"\" );\n\treturn div.firstChild.getAttribute( \"value\" ) === \"\";\n}) ) {\n\taddHandle( \"value\", function( elem, name, isXML ) {\n\t\tif ( !isXML && elem.nodeName.toLowerCase() === \"input\" ) {\n\t\t\treturn elem.defaultValue;\n\t\t}\n\t});\n}\n\n// Support: IE<9\n// Use getAttributeNode to fetch booleans when getAttribute lies\nif ( !assert(function( div ) {\n\treturn div.getAttribute(\"disabled\") == null;\n}) ) {\n\taddHandle( booleans, function( elem, name, isXML ) {\n\t\tvar val;\n\t\tif ( !isXML ) {\n\t\t\treturn elem[ name ] === true ? name.toLowerCase() :\n\t\t\t\t\t(val = elem.getAttributeNode( name )) && val.specified ?\n\t\t\t\t\tval.value :\n\t\t\t\tnull;\n\t\t}\n\t});\n}\n\nreturn Sizzle;\n\n})( window );\n\n\n\njQuery.find = Sizzle;\njQuery.expr = Sizzle.selectors;\njQuery.expr[ \":\" ] = jQuery.expr.pseudos;\njQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;\njQuery.text = Sizzle.getText;\njQuery.isXMLDoc = Sizzle.isXML;\njQuery.contains = Sizzle.contains;\n\n\n\nvar dir = function( elem, dir, until ) {\n\tvar matched = [],\n\t\ttruncate = until !== undefined;\n\n\twhile ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {\n\t\tif ( elem.nodeType === 1 ) {\n\t\t\tif ( truncate && jQuery( elem ).is( until ) ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmatched.push( elem );\n\t\t}\n\t}\n\treturn matched;\n};\n\n\nvar siblings = function( n, elem ) {\n\tvar matched = [];\n\n\tfor ( ; n; n = n.nextSibling ) {\n\t\tif ( n.nodeType === 1 && n !== elem ) {\n\t\t\tmatched.push( n );\n\t\t}\n\t}\n\n\treturn matched;\n};\n\n\nvar rneedsContext = jQuery.expr.match.needsContext;\n\nvar rsingleTag = ( /^<([\\w-]+)\\s*\\/?>(?:<\\/\\1>|)$/ );\n\n\n\nvar risSimple = /^.[^:#\\[\\.,]*$/;\n\n// Implement the identical functionality for filter and not\nfunction winnow( elements, qualifier, not ) {\n\tif ( jQuery.isFunction( qualifier ) ) {\n\t\treturn jQuery.grep( elements, function( elem, i ) {\n\t\t\t/* jshint -W018 */\n\t\t\treturn !!qualifier.call( elem, i, elem ) !== not;\n\t\t} );\n\n\t}\n\n\tif ( qualifier.nodeType ) {\n\t\treturn jQuery.grep( elements, function( elem ) {\n\t\t\treturn ( elem === qualifier ) !== not;\n\t\t} );\n\n\t}\n\n\tif ( typeof qualifier === \"string\" ) {\n\t\tif ( risSimple.test( qualifier ) ) {\n\t\t\treturn jQuery.filter( qualifier, elements, not );\n\t\t}\n\n\t\tqualifier = jQuery.filter( qualifier, elements );\n\t}\n\n\treturn jQuery.grep( elements, function( elem ) {\n\t\treturn ( indexOf.call( qualifier, elem ) > -1 ) !== not;\n\t} );\n}\n\njQuery.filter = function( expr, elems, not ) {\n\tvar elem = elems[ 0 ];\n\n\tif ( not ) {\n\t\texpr = \":not(\" + expr + \")\";\n\t}\n\n\treturn elems.length === 1 && elem.nodeType === 1 ?\n\t\tjQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :\n\t\tjQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {\n\t\t\treturn elem.nodeType === 1;\n\t\t} ) );\n};\n\njQuery.fn.extend( {\n\tfind: function( selector ) {\n\t\tvar i,\n\t\t\tlen = this.length,\n\t\t\tret = [],\n\t\t\tself = this;\n\n\t\tif ( typeof selector !== \"string\" ) {\n\t\t\treturn this.pushStack( jQuery( selector ).filter( function() {\n\t\t\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\t\t\tif ( jQuery.contains( self[ i ], this ) ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} ) );\n\t\t}\n\n\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\tjQuery.find( selector, self[ i ], ret );\n\t\t}\n\n\t\t// Needed because $( selector, context ) becomes $( context ).find( selector )\n\t\tret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );\n\t\tret.selector = this.selector ? this.selector + \" \" + selector : selector;\n\t\treturn ret;\n\t},\n\tfilter: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], false ) );\n\t},\n\tnot: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], true ) );\n\t},\n\tis: function( selector ) {\n\t\treturn !!winnow(\n\t\t\tthis,\n\n\t\t\t// If this is a positional/relative selector, check membership in the returned set\n\t\t\t// so $(\"p:first\").is(\"p:last\") won't return true for a doc with two \"p\".\n\t\t\ttypeof selector === \"string\" && rneedsContext.test( selector ) ?\n\t\t\t\tjQuery( selector ) :\n\t\t\t\tselector || [],\n\t\t\tfalse\n\t\t).length;\n\t}\n} );\n\n\n// Initialize a jQuery object\n\n\n// A central reference to the root jQuery(document)\nvar rootjQuery,\n\n\t// A simple way to check for HTML strings\n\t// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)\n\t// Strict HTML recognition (#11290: must start with <)\n\trquickExpr = /^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]*))$/,\n\n\tinit = jQuery.fn.init = function( selector, context, root ) {\n\t\tvar match, elem;\n\n\t\t// HANDLE: $(\"\"), $(null), $(undefined), $(false)\n\t\tif ( !selector ) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// Method init() accepts an alternate rootjQuery\n\t\t// so migrate can support jQuery.sub (gh-2101)\n\t\troot = root || rootjQuery;\n\n\t\t// Handle HTML strings\n\t\tif ( typeof selector === \"string\" ) {\n\t\t\tif ( selector[ 0 ] === \"<\" &&\n\t\t\t\tselector[ selector.length - 1 ] === \">\" &&\n\t\t\t\tselector.length >= 3 ) {\n\n\t\t\t\t// Assume that strings that start and end with <> are HTML and skip the regex check\n\t\t\t\tmatch = [ null, selector, null ];\n\n\t\t\t} else {\n\t\t\t\tmatch = rquickExpr.exec( selector );\n\t\t\t}\n\n\t\t\t// Match html or make sure no context is specified for #id\n\t\t\tif ( match && ( match[ 1 ] || !context ) ) {\n\n\t\t\t\t// HANDLE: $(html) -> $(array)\n\t\t\t\tif ( match[ 1 ] ) {\n\t\t\t\t\tcontext = context instanceof jQuery ? context[ 0 ] : context;\n\n\t\t\t\t\t// Option to run scripts is true for back-compat\n\t\t\t\t\t// Intentionally let the error be thrown if parseHTML is not present\n\t\t\t\t\tjQuery.merge( this, jQuery.parseHTML(\n\t\t\t\t\t\tmatch[ 1 ],\n\t\t\t\t\t\tcontext && context.nodeType ? context.ownerDocument || context : document,\n\t\t\t\t\t\ttrue\n\t\t\t\t\t) );\n\n\t\t\t\t\t// HANDLE: $(html, props)\n\t\t\t\t\tif ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {\n\t\t\t\t\t\tfor ( match in context ) {\n\n\t\t\t\t\t\t\t// Properties of context are called as methods if possible\n\t\t\t\t\t\t\tif ( jQuery.isFunction( this[ match ] ) ) {\n\t\t\t\t\t\t\t\tthis[ match ]( context[ match ] );\n\n\t\t\t\t\t\t\t// ...and otherwise set as attributes\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.attr( match, context[ match ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn this;\n\n\t\t\t\t// HANDLE: $(#id)\n\t\t\t\t} else {\n\t\t\t\t\telem = document.getElementById( match[ 2 ] );\n\n\t\t\t\t\t// Support: Blackberry 4.6\n\t\t\t\t\t// gEBID returns nodes no longer in the document (#6963)\n\t\t\t\t\tif ( elem && elem.parentNode ) {\n\n\t\t\t\t\t\t// Inject the element directly into the jQuery object\n\t\t\t\t\t\tthis.length = 1;\n\t\t\t\t\t\tthis[ 0 ] = elem;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.context = document;\n\t\t\t\t\tthis.selector = selector;\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t// HANDLE: $(expr, $(...))\n\t\t\t} else if ( !context || context.jquery ) {\n\t\t\t\treturn ( context || root ).find( selector );\n\n\t\t\t// HANDLE: $(expr, context)\n\t\t\t// (which is just equivalent to: $(context).find(expr)\n\t\t\t} else {\n\t\t\t\treturn this.constructor( context ).find( selector );\n\t\t\t}\n\n\t\t// HANDLE: $(DOMElement)\n\t\t} else if ( selector.nodeType ) {\n\t\t\tthis.context = this[ 0 ] = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\n\t\t// HANDLE: $(function)\n\t\t// Shortcut for document ready\n\t\t} else if ( jQuery.isFunction( selector ) ) {\n\t\t\treturn root.ready !== undefined ?\n\t\t\t\troot.ready( selector ) :\n\n\t\t\t\t// Execute immediately if ready is not present\n\t\t\t\tselector( jQuery );\n\t\t}\n\n\t\tif ( selector.selector !== undefined ) {\n\t\t\tthis.selector = selector.selector;\n\t\t\tthis.context = selector.context;\n\t\t}\n\n\t\treturn jQuery.makeArray( selector, this );\n\t};\n\n// Give the init function the jQuery prototype for later instantiation\ninit.prototype = jQuery.fn;\n\n// Initialize central reference\nrootjQuery = jQuery( document );\n\n\nvar rparentsprev = /^(?:parents|prev(?:Until|All))/,\n\n\t// Methods guaranteed to produce a unique set when starting from a unique set\n\tguaranteedUnique = {\n\t\tchildren: true,\n\t\tcontents: true,\n\t\tnext: true,\n\t\tprev: true\n\t};\n\njQuery.fn.extend( {\n\thas: function( target ) {\n\t\tvar targets = jQuery( target, this ),\n\t\t\tl = targets.length;\n\n\t\treturn this.filter( function() {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tif ( jQuery.contains( this, targets[ i ] ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\tclosest: function( selectors, context ) {\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\tl = this.length,\n\t\t\tmatched = [],\n\t\t\tpos = rneedsContext.test( selectors ) || typeof selectors !== \"string\" ?\n\t\t\t\tjQuery( selectors, context || this.context ) :\n\t\t\t\t0;\n\n\t\tfor ( ; i < l; i++ ) {\n\t\t\tfor ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {\n\n\t\t\t\t// Always skip document fragments\n\t\t\t\tif ( cur.nodeType < 11 && ( pos ?\n\t\t\t\t\tpos.index( cur ) > -1 :\n\n\t\t\t\t\t// Don't pass non-elements to Sizzle\n\t\t\t\t\tcur.nodeType === 1 &&\n\t\t\t\t\t\tjQuery.find.matchesSelector( cur, selectors ) ) ) {\n\n\t\t\t\t\tmatched.push( cur );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );\n\t},\n\n\t// Determine the position of an element within the set\n\tindex: function( elem ) {\n\n\t\t// No argument, return index in parent\n\t\tif ( !elem ) {\n\t\t\treturn ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;\n\t\t}\n\n\t\t// Index in selector\n\t\tif ( typeof elem === \"string\" ) {\n\t\t\treturn indexOf.call( jQuery( elem ), this[ 0 ] );\n\t\t}\n\n\t\t// Locate the position of the desired element\n\t\treturn indexOf.call( this,\n\n\t\t\t// If it receives a jQuery object, the first element is used\n\t\t\telem.jquery ? elem[ 0 ] : elem\n\t\t);\n\t},\n\n\tadd: function( selector, context ) {\n\t\treturn this.pushStack(\n\t\t\tjQuery.uniqueSort(\n\t\t\t\tjQuery.merge( this.get(), jQuery( selector, context ) )\n\t\t\t)\n\t\t);\n\t},\n\n\taddBack: function( selector ) {\n\t\treturn this.add( selector == null ?\n\t\t\tthis.prevObject : this.prevObject.filter( selector )\n\t\t);\n\t}\n} );\n\nfunction sibling( cur, dir ) {\n\twhile ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}\n\treturn cur;\n}\n\njQuery.each( {\n\tparent: function( elem ) {\n\t\tvar parent = elem.parentNode;\n\t\treturn parent && parent.nodeType !== 11 ? parent : null;\n\t},\n\tparents: function( elem ) {\n\t\treturn dir( elem, \"parentNode\" );\n\t},\n\tparentsUntil: function( elem, i, until ) {\n\t\treturn dir( elem, \"parentNode\", until );\n\t},\n\tnext: function( elem ) {\n\t\treturn sibling( elem, \"nextSibling\" );\n\t},\n\tprev: function( elem ) {\n\t\treturn sibling( elem, \"previousSibling\" );\n\t},\n\tnextAll: function( elem ) {\n\t\treturn dir( elem, \"nextSibling\" );\n\t},\n\tprevAll: function( elem ) {\n\t\treturn dir( elem, \"previousSibling\" );\n\t},\n\tnextUntil: function( elem, i, until ) {\n\t\treturn dir( elem, \"nextSibling\", until );\n\t},\n\tprevUntil: function( elem, i, until ) {\n\t\treturn dir( elem, \"previousSibling\", until );\n\t},\n\tsiblings: function( elem ) {\n\t\treturn siblings( ( elem.parentNode || {} ).firstChild, elem );\n\t},\n\tchildren: function( elem ) {\n\t\treturn siblings( elem.firstChild );\n\t},\n\tcontents: function( elem ) {\n\t\treturn elem.contentDocument || jQuery.merge( [], elem.childNodes );\n\t}\n}, function( name, fn ) {\n\tjQuery.fn[ name ] = function( until, selector ) {\n\t\tvar matched = jQuery.map( this, fn, until );\n\n\t\tif ( name.slice( -5 ) !== \"Until\" ) {\n\t\t\tselector = until;\n\t\t}\n\n\t\tif ( selector && typeof selector === \"string\" ) {\n\t\t\tmatched = jQuery.filter( selector, matched );\n\t\t}\n\n\t\tif ( this.length > 1 ) {\n\n\t\t\t// Remove duplicates\n\t\t\tif ( !guaranteedUnique[ name ] ) {\n\t\t\t\tjQuery.uniqueSort( matched );\n\t\t\t}\n\n\t\t\t// Reverse order for parents* and prev-derivatives\n\t\t\tif ( rparentsprev.test( name ) ) {\n\t\t\t\tmatched.reverse();\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched );\n\t};\n} );\nvar rnotwhite = ( /\\S+/g );\n\n\n\n// Convert String-formatted options into Object-formatted ones\nfunction createOptions( options ) {\n\tvar object = {};\n\tjQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {\n\t\tobject[ flag ] = true;\n\t} );\n\treturn object;\n}\n\n/*\n * Create a callback list using the following parameters:\n *\n *\toptions: an optional list of space-separated options that will change how\n *\t\t\tthe callback list behaves or a more traditional option object\n *\n * By default a callback list will act like an event callback list and can be\n * \"fired\" multiple times.\n *\n * Possible options:\n *\n *\tonce:\t\t\twill ensure the callback list can only be fired once (like a Deferred)\n *\n *\tmemory:\t\t\twill keep track of previous values and will call any callback added\n *\t\t\t\t\tafter the list has been fired right away with the latest \"memorized\"\n *\t\t\t\t\tvalues (like a Deferred)\n *\n *\tunique:\t\t\twill ensure a callback can only be added once (no duplicate in the list)\n *\n *\tstopOnFalse:\tinterrupt callings when a callback returns false\n *\n */\njQuery.Callbacks = function( options ) {\n\n\t// Convert options from String-formatted to Object-formatted if needed\n\t// (we check in cache first)\n\toptions = typeof options === \"string\" ?\n\t\tcreateOptions( options ) :\n\t\tjQuery.extend( {}, options );\n\n\tvar // Flag to know if list is currently firing\n\t\tfiring,\n\n\t\t// Last fire value for non-forgettable lists\n\t\tmemory,\n\n\t\t// Flag to know if list was already fired\n\t\tfired,\n\n\t\t// Flag to prevent firing\n\t\tlocked,\n\n\t\t// Actual callback list\n\t\tlist = [],\n\n\t\t// Queue of execution data for repeatable lists\n\t\tqueue = [],\n\n\t\t// Index of currently firing callback (modified by add/remove as needed)\n\t\tfiringIndex = -1,\n\n\t\t// Fire callbacks\n\t\tfire = function() {\n\n\t\t\t// Enforce single-firing\n\t\t\tlocked = options.once;\n\n\t\t\t// Execute callbacks for all pending executions,\n\t\t\t// respecting firingIndex overrides and runtime changes\n\t\t\tfired = firing = true;\n\t\t\tfor ( ; queue.length; firingIndex = -1 ) {\n\t\t\t\tmemory = queue.shift();\n\t\t\t\twhile ( ++firingIndex < list.length ) {\n\n\t\t\t\t\t// Run callback and check for early termination\n\t\t\t\t\tif ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&\n\t\t\t\t\t\toptions.stopOnFalse ) {\n\n\t\t\t\t\t\t// Jump to end and forget the data so .add doesn't re-fire\n\t\t\t\t\t\tfiringIndex = list.length;\n\t\t\t\t\t\tmemory = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Forget the data if we're done with it\n\t\t\tif ( !options.memory ) {\n\t\t\t\tmemory = false;\n\t\t\t}\n\n\t\t\tfiring = false;\n\n\t\t\t// Clean up if we're done firing for good\n\t\t\tif ( locked ) {\n\n\t\t\t\t// Keep an empty list if we have data for future add calls\n\t\t\t\tif ( memory ) {\n\t\t\t\t\tlist = [];\n\n\t\t\t\t// Otherwise, this object is spent\n\t\t\t\t} else {\n\t\t\t\t\tlist = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t// Actual Callbacks object\n\t\tself = {\n\n\t\t\t// Add a callback or a collection of callbacks to the list\n\t\t\tadd: function() {\n\t\t\t\tif ( list ) {\n\n\t\t\t\t\t// If we have memory from a past run, we should fire after adding\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfiringIndex = list.length - 1;\n\t\t\t\t\t\tqueue.push( memory );\n\t\t\t\t\t}\n\n\t\t\t\t\t( function add( args ) {\n\t\t\t\t\t\tjQuery.each( args, function( _, arg ) {\n\t\t\t\t\t\t\tif ( jQuery.isFunction( arg ) ) {\n\t\t\t\t\t\t\t\tif ( !options.unique || !self.has( arg ) ) {\n\t\t\t\t\t\t\t\t\tlist.push( arg );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if ( arg && arg.length && jQuery.type( arg ) !== \"string\" ) {\n\n\t\t\t\t\t\t\t\t// Inspect recursively\n\t\t\t\t\t\t\t\tadd( arg );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} );\n\t\t\t\t\t} )( arguments );\n\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Remove a callback from the list\n\t\t\tremove: function() {\n\t\t\t\tjQuery.each( arguments, function( _, arg ) {\n\t\t\t\t\tvar index;\n\t\t\t\t\twhile ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {\n\t\t\t\t\t\tlist.splice( index, 1 );\n\n\t\t\t\t\t\t// Handle firing indexes\n\t\t\t\t\t\tif ( index <= firingIndex ) {\n\t\t\t\t\t\t\tfiringIndex--;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Check if a given callback is in the list.\n\t\t\t// If no argument is given, return whether or not list has callbacks attached.\n\t\t\thas: function( fn ) {\n\t\t\t\treturn fn ?\n\t\t\t\t\tjQuery.inArray( fn, list ) > -1 :\n\t\t\t\t\tlist.length > 0;\n\t\t\t},\n\n\t\t\t// Remove all callbacks from the list\n\t\t\tempty: function() {\n\t\t\t\tif ( list ) {\n\t\t\t\t\tlist = [];\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Disable .fire and .add\n\t\t\t// Abort any current/pending executions\n\t\t\t// Clear all callbacks and values\n\t\t\tdisable: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tlist = memory = \"\";\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tdisabled: function() {\n\t\t\t\treturn !list;\n\t\t\t},\n\n\t\t\t// Disable .fire\n\t\t\t// Also disable .add unless we have memory (since it would have no effect)\n\t\t\t// Abort any pending executions\n\t\t\tlock: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tif ( !memory ) {\n\t\t\t\t\tlist = memory = \"\";\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tlocked: function() {\n\t\t\t\treturn !!locked;\n\t\t\t},\n\n\t\t\t// Call all callbacks with the given context and arguments\n\t\t\tfireWith: function( context, args ) {\n\t\t\t\tif ( !locked ) {\n\t\t\t\t\targs = args || [];\n\t\t\t\t\targs = [ context, args.slice ? args.slice() : args ];\n\t\t\t\t\tqueue.push( args );\n\t\t\t\t\tif ( !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Call all the callbacks with the given arguments\n\t\t\tfire: function() {\n\t\t\t\tself.fireWith( this, arguments );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// To know if the callbacks have already been called at least once\n\t\t\tfired: function() {\n\t\t\t\treturn !!fired;\n\t\t\t}\n\t\t};\n\n\treturn self;\n};\n\n\njQuery.extend( {\n\n\tDeferred: function( func ) {\n\t\tvar tuples = [\n\n\t\t\t\t// action, add listener, listener list, final state\n\t\t\t\t[ \"resolve\", \"done\", jQuery.Callbacks( \"once memory\" ), \"resolved\" ],\n\t\t\t\t[ \"reject\", \"fail\", jQuery.Callbacks( \"once memory\" ), \"rejected\" ],\n\t\t\t\t[ \"notify\", \"progress\", jQuery.Callbacks( \"memory\" ) ]\n\t\t\t],\n\t\t\tstate = \"pending\",\n\t\t\tpromise = {\n\t\t\t\tstate: function() {\n\t\t\t\t\treturn state;\n\t\t\t\t},\n\t\t\t\talways: function() {\n\t\t\t\t\tdeferred.done( arguments ).fail( arguments );\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\t\t\t\tthen: function( /* fnDone, fnFail, fnProgress */ ) {\n\t\t\t\t\tvar fns = arguments;\n\t\t\t\t\treturn jQuery.Deferred( function( newDefer ) {\n\t\t\t\t\t\tjQuery.each( tuples, function( i, tuple ) {\n\t\t\t\t\t\t\tvar fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];\n\n\t\t\t\t\t\t\t// deferred[ done | fail | progress ] for forwarding actions to newDefer\n\t\t\t\t\t\t\tdeferred[ tuple[ 1 ] ]( function() {\n\t\t\t\t\t\t\t\tvar returned = fn && fn.apply( this, arguments );\n\t\t\t\t\t\t\t\tif ( returned && jQuery.isFunction( returned.promise ) ) {\n\t\t\t\t\t\t\t\t\treturned.promise()\n\t\t\t\t\t\t\t\t\t\t.progress( newDefer.notify )\n\t\t\t\t\t\t\t\t\t\t.done( newDefer.resolve )\n\t\t\t\t\t\t\t\t\t\t.fail( newDefer.reject );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tnewDefer[ tuple[ 0 ] + \"With\" ](\n\t\t\t\t\t\t\t\t\t\tthis === promise ? newDefer.promise() : this,\n\t\t\t\t\t\t\t\t\t\tfn ? [ returned ] : arguments\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} );\n\t\t\t\t\t\tfns = null;\n\t\t\t\t\t} ).promise();\n\t\t\t\t},\n\n\t\t\t\t// Get a promise for this deferred\n\t\t\t\t// If obj is provided, the promise aspect is added to the object\n\t\t\t\tpromise: function( obj ) {\n\t\t\t\t\treturn obj != null ? jQuery.extend( obj, promise ) : promise;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdeferred = {};\n\n\t\t// Keep pipe for back-compat\n\t\tpromise.pipe = promise.then;\n\n\t\t// Add list-specific methods\n\t\tjQuery.each( tuples, function( i, tuple ) {\n\t\t\tvar list = tuple[ 2 ],\n\t\t\t\tstateString = tuple[ 3 ];\n\n\t\t\t// promise[ done | fail | progress ] = list.add\n\t\t\tpromise[ tuple[ 1 ] ] = list.add;\n\n\t\t\t// Handle state\n\t\t\tif ( stateString ) {\n\t\t\t\tlist.add( function() {\n\n\t\t\t\t\t// state = [ resolved | rejected ]\n\t\t\t\t\tstate = stateString;\n\n\t\t\t\t// [ reject_list | resolve_list ].disable; progress_list.lock\n\t\t\t\t}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );\n\t\t\t}\n\n\t\t\t// deferred[ resolve | reject | notify ]\n\t\t\tdeferred[ tuple[ 0 ] ] = function() {\n\t\t\t\tdeferred[ tuple[ 0 ] + \"With\" ]( this === deferred ? promise : this, arguments );\n\t\t\t\treturn this;\n\t\t\t};\n\t\t\tdeferred[ tuple[ 0 ] + \"With\" ] = list.fireWith;\n\t\t} );\n\n\t\t// Make the deferred a promise\n\t\tpromise.promise( deferred );\n\n\t\t// Call given func if any\n\t\tif ( func ) {\n\t\t\tfunc.call( deferred, deferred );\n\t\t}\n\n\t\t// All done!\n\t\treturn deferred;\n\t},\n\n\t// Deferred helper\n\twhen: function( subordinate /* , ..., subordinateN */ ) {\n\t\tvar i = 0,\n\t\t\tresolveValues = slice.call( arguments ),\n\t\t\tlength = resolveValues.length,\n\n\t\t\t// the count of uncompleted subordinates\n\t\t\tremaining = length !== 1 ||\n\t\t\t\t( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,\n\n\t\t\t// the master Deferred.\n\t\t\t// If resolveValues consist of only a single Deferred, just use that.\n\t\t\tdeferred = remaining === 1 ? subordinate : jQuery.Deferred(),\n\n\t\t\t// Update function for both resolve and progress values\n\t\t\tupdateFunc = function( i, contexts, values ) {\n\t\t\t\treturn function( value ) {\n\t\t\t\t\tcontexts[ i ] = this;\n\t\t\t\t\tvalues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;\n\t\t\t\t\tif ( values === progressValues ) {\n\t\t\t\t\t\tdeferred.notifyWith( contexts, values );\n\t\t\t\t\t} else if ( !( --remaining ) ) {\n\t\t\t\t\t\tdeferred.resolveWith( contexts, values );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t},\n\n\t\t\tprogressValues, progressContexts, resolveContexts;\n\n\t\t// Add listeners to Deferred subordinates; treat others as resolved\n\t\tif ( length > 1 ) {\n\t\t\tprogressValues = new Array( length );\n\t\t\tprogressContexts = new Array( length );\n\t\t\tresolveContexts = new Array( length );\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tif ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {\n\t\t\t\t\tresolveValues[ i ].promise()\n\t\t\t\t\t\t.progress( updateFunc( i, progressContexts, progressValues ) )\n\t\t\t\t\t\t.done( updateFunc( i, resolveContexts, resolveValues ) )\n\t\t\t\t\t\t.fail( deferred.reject );\n\t\t\t\t} else {\n\t\t\t\t\t--remaining;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If we're not waiting on anything, resolve the master\n\t\tif ( !remaining ) {\n\t\t\tdeferred.resolveWith( resolveContexts, resolveValues );\n\t\t}\n\n\t\treturn deferred.promise();\n\t}\n} );\n\n\n// The deferred used on DOM ready\nvar readyList;\n\njQuery.fn.ready = function( fn ) {\n\n\t// Add the callback\n\tjQuery.ready.promise().done( fn );\n\n\treturn this;\n};\n\njQuery.extend( {\n\n\t// Is the DOM ready to be used? Set to true once it occurs.\n\tisReady: false,\n\n\t// A counter to track how many items to wait for before\n\t// the ready event fires. See #6781\n\treadyWait: 1,\n\n\t// Hold (or release) the ready event\n\tholdReady: function( hold ) {\n\t\tif ( hold ) {\n\t\t\tjQuery.readyWait++;\n\t\t} else {\n\t\t\tjQuery.ready( true );\n\t\t}\n\t},\n\n\t// Handle when the DOM is ready\n\tready: function( wait ) {\n\n\t\t// Abort if there are pending holds or we're already ready\n\t\tif ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Remember that the DOM is ready\n\t\tjQuery.isReady = true;\n\n\t\t// If a normal DOM Ready event fired, decrement, and wait if need be\n\t\tif ( wait !== true && --jQuery.readyWait > 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there are functions bound, to execute\n\t\treadyList.resolveWith( document, [ jQuery ] );\n\n\t\t// Trigger any bound ready events\n\t\tif ( jQuery.fn.triggerHandler ) {\n\t\t\tjQuery( document ).triggerHandler( \"ready\" );\n\t\t\tjQuery( document ).off( \"ready\" );\n\t\t}\n\t}\n} );\n\n/**\n * The ready event handler and self cleanup method\n */\nfunction completed() {\n\tdocument.removeEventListener( \"DOMContentLoaded\", completed );\n\twindow.removeEventListener( \"load\", completed );\n\tjQuery.ready();\n}\n\njQuery.ready.promise = function( obj ) {\n\tif ( !readyList ) {\n\n\t\treadyList = jQuery.Deferred();\n\n\t\t// Catch cases where $(document).ready() is called\n\t\t// after the browser event has already occurred.\n\t\t// Support: IE9-10 only\n\t\t// Older IE sometimes signals \"interactive\" too soon\n\t\tif ( document.readyState === \"complete\" ||\n\t\t\t( document.readyState !== \"loading\" && !document.documentElement.doScroll ) ) {\n\n\t\t\t// Handle it asynchronously to allow scripts the opportunity to delay ready\n\t\t\twindow.setTimeout( jQuery.ready );\n\n\t\t} else {\n\n\t\t\t// Use the handy event callback\n\t\t\tdocument.addEventListener( \"DOMContentLoaded\", completed );\n\n\t\t\t// A fallback to window.onload, that will always work\n\t\t\twindow.addEventListener( \"load\", completed );\n\t\t}\n\t}\n\treturn readyList.promise( obj );\n};\n\n// Kick off the DOM ready check even if the user does not\njQuery.ready.promise();\n\n\n\n\n// Multifunctional method to get and set values of a collection\n// The value/s can optionally be executed if it's a function\nvar access = function( elems, fn, key, value, chainable, emptyGet, raw ) {\n\tvar i = 0,\n\t\tlen = elems.length,\n\t\tbulk = key == null;\n\n\t// Sets many values\n\tif ( jQuery.type( key ) === \"object\" ) {\n\t\tchainable = true;\n\t\tfor ( i in key ) {\n\t\t\taccess( elems, fn, i, key[ i ], true, emptyGet, raw );\n\t\t}\n\n\t// Sets one value\n\t} else if ( value !== undefined ) {\n\t\tchainable = true;\n\n\t\tif ( !jQuery.isFunction( value ) ) {\n\t\t\traw = true;\n\t\t}\n\n\t\tif ( bulk ) {\n\n\t\t\t// Bulk operations run against the entire set\n\t\t\tif ( raw ) {\n\t\t\t\tfn.call( elems, value );\n\t\t\t\tfn = null;\n\n\t\t\t// ...except when executing function values\n\t\t\t} else {\n\t\t\t\tbulk = fn;\n\t\t\t\tfn = function( elem, key, value ) {\n\t\t\t\t\treturn bulk.call( jQuery( elem ), value );\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif ( fn ) {\n\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\tfn(\n\t\t\t\t\telems[ i ], key, raw ?\n\t\t\t\t\tvalue :\n\t\t\t\t\tvalue.call( elems[ i ], i, fn( elems[ i ], key ) )\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn chainable ?\n\t\telems :\n\n\t\t// Gets\n\t\tbulk ?\n\t\t\tfn.call( elems ) :\n\t\t\tlen ? fn( elems[ 0 ], key ) : emptyGet;\n};\nvar acceptData = function( owner ) {\n\n\t// Accepts only:\n\t//  - Node\n\t//    - Node.ELEMENT_NODE\n\t//    - Node.DOCUMENT_NODE\n\t//  - Object\n\t//    - Any\n\t/* jshint -W018 */\n\treturn owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );\n};\n\n\n\n\nfunction Data() {\n\tthis.expando = jQuery.expando + Data.uid++;\n}\n\nData.uid = 1;\n\nData.prototype = {\n\n\tregister: function( owner, initial ) {\n\t\tvar value = initial || {};\n\n\t\t// If it is a node unlikely to be stringify-ed or looped over\n\t\t// use plain assignment\n\t\tif ( owner.nodeType ) {\n\t\t\towner[ this.expando ] = value;\n\n\t\t// Otherwise secure it in a non-enumerable, non-writable property\n\t\t// configurability must be true to allow the property to be\n\t\t// deleted with the delete operator\n\t\t} else {\n\t\t\tObject.defineProperty( owner, this.expando, {\n\t\t\t\tvalue: value,\n\t\t\t\twritable: true,\n\t\t\t\tconfigurable: true\n\t\t\t} );\n\t\t}\n\t\treturn owner[ this.expando ];\n\t},\n\tcache: function( owner ) {\n\n\t\t// We can accept data for non-element nodes in modern browsers,\n\t\t// but we should not, see #8335.\n\t\t// Always return an empty object.\n\t\tif ( !acceptData( owner ) ) {\n\t\t\treturn {};\n\t\t}\n\n\t\t// Check if the owner object already has a cache\n\t\tvar value = owner[ this.expando ];\n\n\t\t// If not, create one\n\t\tif ( !value ) {\n\t\t\tvalue = {};\n\n\t\t\t// We can accept data for non-element nodes in modern browsers,\n\t\t\t// but we should not, see #8335.\n\t\t\t// Always return an empty object.\n\t\t\tif ( acceptData( owner ) ) {\n\n\t\t\t\t// If it is a node unlikely to be stringify-ed or looped over\n\t\t\t\t// use plain assignment\n\t\t\t\tif ( owner.nodeType ) {\n\t\t\t\t\towner[ this.expando ] = value;\n\n\t\t\t\t// Otherwise secure it in a non-enumerable property\n\t\t\t\t// configurable must be true to allow the property to be\n\t\t\t\t// deleted when data is removed\n\t\t\t\t} else {\n\t\t\t\t\tObject.defineProperty( owner, this.expando, {\n\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\tconfigurable: true\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn value;\n\t},\n\tset: function( owner, data, value ) {\n\t\tvar prop,\n\t\t\tcache = this.cache( owner );\n\n\t\t// Handle: [ owner, key, value ] args\n\t\tif ( typeof data === \"string\" ) {\n\t\t\tcache[ data ] = value;\n\n\t\t// Handle: [ owner, { properties } ] args\n\t\t} else {\n\n\t\t\t// Copy the properties one-by-one to the cache object\n\t\t\tfor ( prop in data ) {\n\t\t\t\tcache[ prop ] = data[ prop ];\n\t\t\t}\n\t\t}\n\t\treturn cache;\n\t},\n\tget: function( owner, key ) {\n\t\treturn key === undefined ?\n\t\t\tthis.cache( owner ) :\n\t\t\towner[ this.expando ] && owner[ this.expando ][ key ];\n\t},\n\taccess: function( owner, key, value ) {\n\t\tvar stored;\n\n\t\t// In cases where either:\n\t\t//\n\t\t//   1. No key was specified\n\t\t//   2. A string key was specified, but no value provided\n\t\t//\n\t\t// Take the \"read\" path and allow the get method to determine\n\t\t// which value to return, respectively either:\n\t\t//\n\t\t//   1. The entire cache object\n\t\t//   2. The data stored at the key\n\t\t//\n\t\tif ( key === undefined ||\n\t\t\t\t( ( key && typeof key === \"string\" ) && value === undefined ) ) {\n\n\t\t\tstored = this.get( owner, key );\n\n\t\t\treturn stored !== undefined ?\n\t\t\t\tstored : this.get( owner, jQuery.camelCase( key ) );\n\t\t}\n\n\t\t// When the key is not a string, or both a key and value\n\t\t// are specified, set or extend (existing objects) with either:\n\t\t//\n\t\t//   1. An object of properties\n\t\t//   2. A key and value\n\t\t//\n\t\tthis.set( owner, key, value );\n\n\t\t// Since the \"set\" path can have two possible entry points\n\t\t// return the expected data based on which path was taken[*]\n\t\treturn value !== undefined ? value : key;\n\t},\n\tremove: function( owner, key ) {\n\t\tvar i, name, camel,\n\t\t\tcache = owner[ this.expando ];\n\n\t\tif ( cache === undefined ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( key === undefined ) {\n\t\t\tthis.register( owner );\n\n\t\t} else {\n\n\t\t\t// Support array or space separated string of keys\n\t\t\tif ( jQuery.isArray( key ) ) {\n\n\t\t\t\t// If \"name\" is an array of keys...\n\t\t\t\t// When data is initially created, via (\"key\", \"val\") signature,\n\t\t\t\t// keys will be converted to camelCase.\n\t\t\t\t// Since there is no way to tell _how_ a key was added, remove\n\t\t\t\t// both plain key and camelCase key. #12786\n\t\t\t\t// This will only penalize the array argument path.\n\t\t\t\tname = key.concat( key.map( jQuery.camelCase ) );\n\t\t\t} else {\n\t\t\t\tcamel = jQuery.camelCase( key );\n\n\t\t\t\t// Try the string as a key before any manipulation\n\t\t\t\tif ( key in cache ) {\n\t\t\t\t\tname = [ key, camel ];\n\t\t\t\t} else {\n\n\t\t\t\t\t// If a key with the spaces exists, use it.\n\t\t\t\t\t// Otherwise, create an array by matching non-whitespace\n\t\t\t\t\tname = camel;\n\t\t\t\t\tname = name in cache ?\n\t\t\t\t\t\t[ name ] : ( name.match( rnotwhite ) || [] );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ti = name.length;\n\n\t\t\twhile ( i-- ) {\n\t\t\t\tdelete cache[ name[ i ] ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove the expando if there's no more data\n\t\tif ( key === undefined || jQuery.isEmptyObject( cache ) ) {\n\n\t\t\t// Support: Chrome <= 35-45+\n\t\t\t// Webkit & Blink performance suffers when deleting properties\n\t\t\t// from DOM nodes, so set to undefined instead\n\t\t\t// https://code.google.com/p/chromium/issues/detail?id=378607\n\t\t\tif ( owner.nodeType ) {\n\t\t\t\towner[ this.expando ] = undefined;\n\t\t\t} else {\n\t\t\t\tdelete owner[ this.expando ];\n\t\t\t}\n\t\t}\n\t},\n\thasData: function( owner ) {\n\t\tvar cache = owner[ this.expando ];\n\t\treturn cache !== undefined && !jQuery.isEmptyObject( cache );\n\t}\n};\nvar dataPriv = new Data();\n\nvar dataUser = new Data();\n\n\n\n//\tImplementation Summary\n//\n//\t1. Enforce API surface and semantic compatibility with 1.9.x branch\n//\t2. Improve the module's maintainability by reducing the storage\n//\t\tpaths to a single mechanism.\n//\t3. Use the same single mechanism to support \"private\" and \"user\" data.\n//\t4. _Never_ expose \"private\" data to user code (TODO: Drop _data, _removeData)\n//\t5. Avoid exposing implementation details on user objects (eg. expando properties)\n//\t6. Provide a clear path for implementation upgrade to WeakMap in 2014\n\nvar rbrace = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,\n\trmultiDash = /[A-Z]/g;\n\nfunction dataAttr( elem, key, data ) {\n\tvar name;\n\n\t// If nothing was found internally, try to fetch any\n\t// data from the HTML5 data-* attribute\n\tif ( data === undefined && elem.nodeType === 1 ) {\n\t\tname = \"data-\" + key.replace( rmultiDash, \"-$&\" ).toLowerCase();\n\t\tdata = elem.getAttribute( name );\n\n\t\tif ( typeof data === \"string\" ) {\n\t\t\ttry {\n\t\t\t\tdata = data === \"true\" ? true :\n\t\t\t\t\tdata === \"false\" ? false :\n\t\t\t\t\tdata === \"null\" ? null :\n\n\t\t\t\t\t// Only convert to a number if it doesn't change the string\n\t\t\t\t\t+data + \"\" === data ? +data :\n\t\t\t\t\trbrace.test( data ) ? jQuery.parseJSON( data ) :\n\t\t\t\t\tdata;\n\t\t\t} catch ( e ) {}\n\n\t\t\t// Make sure we set the data so it isn't changed later\n\t\t\tdataUser.set( elem, key, data );\n\t\t} else {\n\t\t\tdata = undefined;\n\t\t}\n\t}\n\treturn data;\n}\n\njQuery.extend( {\n\thasData: function( elem ) {\n\t\treturn dataUser.hasData( elem ) || dataPriv.hasData( elem );\n\t},\n\n\tdata: function( elem, name, data ) {\n\t\treturn dataUser.access( elem, name, data );\n\t},\n\n\tremoveData: function( elem, name ) {\n\t\tdataUser.remove( elem, name );\n\t},\n\n\t// TODO: Now that all calls to _data and _removeData have been replaced\n\t// with direct calls to dataPriv methods, these can be deprecated.\n\t_data: function( elem, name, data ) {\n\t\treturn dataPriv.access( elem, name, data );\n\t},\n\n\t_removeData: function( elem, name ) {\n\t\tdataPriv.remove( elem, name );\n\t}\n} );\n\njQuery.fn.extend( {\n\tdata: function( key, value ) {\n\t\tvar i, name, data,\n\t\t\telem = this[ 0 ],\n\t\t\tattrs = elem && elem.attributes;\n\n\t\t// Gets all values\n\t\tif ( key === undefined ) {\n\t\t\tif ( this.length ) {\n\t\t\t\tdata = dataUser.get( elem );\n\n\t\t\t\tif ( elem.nodeType === 1 && !dataPriv.get( elem, \"hasDataAttrs\" ) ) {\n\t\t\t\t\ti = attrs.length;\n\t\t\t\t\twhile ( i-- ) {\n\n\t\t\t\t\t\t// Support: IE11+\n\t\t\t\t\t\t// The attrs elements can be null (#14894)\n\t\t\t\t\t\tif ( attrs[ i ] ) {\n\t\t\t\t\t\t\tname = attrs[ i ].name;\n\t\t\t\t\t\t\tif ( name.indexOf( \"data-\" ) === 0 ) {\n\t\t\t\t\t\t\t\tname = jQuery.camelCase( name.slice( 5 ) );\n\t\t\t\t\t\t\t\tdataAttr( elem, name, data[ name ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdataPriv.set( elem, \"hasDataAttrs\", true );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn data;\n\t\t}\n\n\t\t// Sets multiple values\n\t\tif ( typeof key === \"object\" ) {\n\t\t\treturn this.each( function() {\n\t\t\t\tdataUser.set( this, key );\n\t\t\t} );\n\t\t}\n\n\t\treturn access( this, function( value ) {\n\t\t\tvar data, camelKey;\n\n\t\t\t// The calling jQuery object (element matches) is not empty\n\t\t\t// (and therefore has an element appears at this[ 0 ]) and the\n\t\t\t// `value` parameter was not undefined. An empty jQuery object\n\t\t\t// will result in `undefined` for elem = this[ 0 ] which will\n\t\t\t// throw an exception if an attempt to read a data cache is made.\n\t\t\tif ( elem && value === undefined ) {\n\n\t\t\t\t// Attempt to get data from the cache\n\t\t\t\t// with the key as-is\n\t\t\t\tdata = dataUser.get( elem, key ) ||\n\n\t\t\t\t\t// Try to find dashed key if it exists (gh-2779)\n\t\t\t\t\t// This is for 2.2.x only\n\t\t\t\t\tdataUser.get( elem, key.replace( rmultiDash, \"-$&\" ).toLowerCase() );\n\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\tcamelKey = jQuery.camelCase( key );\n\n\t\t\t\t// Attempt to get data from the cache\n\t\t\t\t// with the key camelized\n\t\t\t\tdata = dataUser.get( elem, camelKey );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// Attempt to \"discover\" the data in\n\t\t\t\t// HTML5 custom data-* attrs\n\t\t\t\tdata = dataAttr( elem, camelKey, undefined );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// We tried really hard, but the data doesn't exist.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Set the data...\n\t\t\tcamelKey = jQuery.camelCase( key );\n\t\t\tthis.each( function() {\n\n\t\t\t\t// First, attempt to store a copy or reference of any\n\t\t\t\t// data that might've been store with a camelCased key.\n\t\t\t\tvar data = dataUser.get( this, camelKey );\n\n\t\t\t\t// For HTML5 data-* attribute interop, we have to\n\t\t\t\t// store property names with dashes in a camelCase form.\n\t\t\t\t// This might not apply to all properties...*\n\t\t\t\tdataUser.set( this, camelKey, value );\n\n\t\t\t\t// *... In the case of properties that might _actually_\n\t\t\t\t// have dashes, we need to also store a copy of that\n\t\t\t\t// unchanged property.\n\t\t\t\tif ( key.indexOf( \"-\" ) > -1 && data !== undefined ) {\n\t\t\t\t\tdataUser.set( this, key, value );\n\t\t\t\t}\n\t\t\t} );\n\t\t}, null, value, arguments.length > 1, null, true );\n\t},\n\n\tremoveData: function( key ) {\n\t\treturn this.each( function() {\n\t\t\tdataUser.remove( this, key );\n\t\t} );\n\t}\n} );\n\n\njQuery.extend( {\n\tqueue: function( elem, type, data ) {\n\t\tvar queue;\n\n\t\tif ( elem ) {\n\t\t\ttype = ( type || \"fx\" ) + \"queue\";\n\t\t\tqueue = dataPriv.get( elem, type );\n\n\t\t\t// Speed up dequeue by getting out quickly if this is just a lookup\n\t\t\tif ( data ) {\n\t\t\t\tif ( !queue || jQuery.isArray( data ) ) {\n\t\t\t\t\tqueue = dataPriv.access( elem, type, jQuery.makeArray( data ) );\n\t\t\t\t} else {\n\t\t\t\t\tqueue.push( data );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn queue || [];\n\t\t}\n\t},\n\n\tdequeue: function( elem, type ) {\n\t\ttype = type || \"fx\";\n\n\t\tvar queue = jQuery.queue( elem, type ),\n\t\t\tstartLength = queue.length,\n\t\t\tfn = queue.shift(),\n\t\t\thooks = jQuery._queueHooks( elem, type ),\n\t\t\tnext = function() {\n\t\t\t\tjQuery.dequeue( elem, type );\n\t\t\t};\n\n\t\t// If the fx queue is dequeued, always remove the progress sentinel\n\t\tif ( fn === \"inprogress\" ) {\n\t\t\tfn = queue.shift();\n\t\t\tstartLength--;\n\t\t}\n\n\t\tif ( fn ) {\n\n\t\t\t// Add a progress sentinel to prevent the fx queue from being\n\t\t\t// automatically dequeued\n\t\t\tif ( type === \"fx\" ) {\n\t\t\t\tqueue.unshift( \"inprogress\" );\n\t\t\t}\n\n\t\t\t// Clear up the last queue stop function\n\t\t\tdelete hooks.stop;\n\t\t\tfn.call( elem, next, hooks );\n\t\t}\n\n\t\tif ( !startLength && hooks ) {\n\t\t\thooks.empty.fire();\n\t\t}\n\t},\n\n\t// Not public - generate a queueHooks object, or return the current one\n\t_queueHooks: function( elem, type ) {\n\t\tvar key = type + \"queueHooks\";\n\t\treturn dataPriv.get( elem, key ) || dataPriv.access( elem, key, {\n\t\t\tempty: jQuery.Callbacks( \"once memory\" ).add( function() {\n\t\t\t\tdataPriv.remove( elem, [ type + \"queue\", key ] );\n\t\t\t} )\n\t\t} );\n\t}\n} );\n\njQuery.fn.extend( {\n\tqueue: function( type, data ) {\n\t\tvar setter = 2;\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tdata = type;\n\t\t\ttype = \"fx\";\n\t\t\tsetter--;\n\t\t}\n\n\t\tif ( arguments.length < setter ) {\n\t\t\treturn jQuery.queue( this[ 0 ], type );\n\t\t}\n\n\t\treturn data === undefined ?\n\t\t\tthis :\n\t\t\tthis.each( function() {\n\t\t\t\tvar queue = jQuery.queue( this, type, data );\n\n\t\t\t\t// Ensure a hooks for this queue\n\t\t\t\tjQuery._queueHooks( this, type );\n\n\t\t\t\tif ( type === \"fx\" && queue[ 0 ] !== \"inprogress\" ) {\n\t\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t\t}\n\t\t\t} );\n\t},\n\tdequeue: function( type ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.dequeue( this, type );\n\t\t} );\n\t},\n\tclearQueue: function( type ) {\n\t\treturn this.queue( type || \"fx\", [] );\n\t},\n\n\t// Get a promise resolved when queues of a certain type\n\t// are emptied (fx is the type by default)\n\tpromise: function( type, obj ) {\n\t\tvar tmp,\n\t\t\tcount = 1,\n\t\t\tdefer = jQuery.Deferred(),\n\t\t\telements = this,\n\t\t\ti = this.length,\n\t\t\tresolve = function() {\n\t\t\t\tif ( !( --count ) ) {\n\t\t\t\t\tdefer.resolveWith( elements, [ elements ] );\n\t\t\t\t}\n\t\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tobj = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\ttype = type || \"fx\";\n\n\t\twhile ( i-- ) {\n\t\t\ttmp = dataPriv.get( elements[ i ], type + \"queueHooks\" );\n\t\t\tif ( tmp && tmp.empty ) {\n\t\t\t\tcount++;\n\t\t\t\ttmp.empty.add( resolve );\n\t\t\t}\n\t\t}\n\t\tresolve();\n\t\treturn defer.promise( obj );\n\t}\n} );\nvar pnum = ( /[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/ ).source;\n\nvar rcssNum = new RegExp( \"^(?:([+-])=|)(\" + pnum + \")([a-z%]*)$\", \"i\" );\n\n\nvar cssExpand = [ \"Top\", \"Right\", \"Bottom\", \"Left\" ];\n\nvar isHidden = function( elem, el ) {\n\n\t\t// isHidden might be called from jQuery#filter function;\n\t\t// in that case, element will be second argument\n\t\telem = el || elem;\n\t\treturn jQuery.css( elem, \"display\" ) === \"none\" ||\n\t\t\t!jQuery.contains( elem.ownerDocument, elem );\n\t};\n\n\n\nfunction adjustCSS( elem, prop, valueParts, tween ) {\n\tvar adjusted,\n\t\tscale = 1,\n\t\tmaxIterations = 20,\n\t\tcurrentValue = tween ?\n\t\t\tfunction() { return tween.cur(); } :\n\t\t\tfunction() { return jQuery.css( elem, prop, \"\" ); },\n\t\tinitial = currentValue(),\n\t\tunit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" ),\n\n\t\t// Starting value computation is required for potential unit mismatches\n\t\tinitialInUnit = ( jQuery.cssNumber[ prop ] || unit !== \"px\" && +initial ) &&\n\t\t\trcssNum.exec( jQuery.css( elem, prop ) );\n\n\tif ( initialInUnit && initialInUnit[ 3 ] !== unit ) {\n\n\t\t// Trust units reported by jQuery.css\n\t\tunit = unit || initialInUnit[ 3 ];\n\n\t\t// Make sure we update the tween properties later on\n\t\tvalueParts = valueParts || [];\n\n\t\t// Iteratively approximate from a nonzero starting point\n\t\tinitialInUnit = +initial || 1;\n\n\t\tdo {\n\n\t\t\t// If previous iteration zeroed out, double until we get *something*.\n\t\t\t// Use string for doubling so we don't accidentally see scale as unchanged below\n\t\t\tscale = scale || \".5\";\n\n\t\t\t// Adjust and apply\n\t\t\tinitialInUnit = initialInUnit / scale;\n\t\t\tjQuery.style( elem, prop, initialInUnit + unit );\n\n\t\t// Update scale, tolerating zero or NaN from tween.cur()\n\t\t// Break the loop if scale is unchanged or perfect, or if we've just had enough.\n\t\t} while (\n\t\t\tscale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations\n\t\t);\n\t}\n\n\tif ( valueParts ) {\n\t\tinitialInUnit = +initialInUnit || +initial || 0;\n\n\t\t// Apply relative offset (+=/-=) if specified\n\t\tadjusted = valueParts[ 1 ] ?\n\t\t\tinitialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :\n\t\t\t+valueParts[ 2 ];\n\t\tif ( tween ) {\n\t\t\ttween.unit = unit;\n\t\t\ttween.start = initialInUnit;\n\t\t\ttween.end = adjusted;\n\t\t}\n\t}\n\treturn adjusted;\n}\nvar rcheckableType = ( /^(?:checkbox|radio)$/i );\n\nvar rtagName = ( /<([\\w:-]+)/ );\n\nvar rscriptType = ( /^$|\\/(?:java|ecma)script/i );\n\n\n\n// We have to close these tags to support XHTML (#13200)\nvar wrapMap = {\n\n\t// Support: IE9\n\toption: [ 1, \"<select multiple='multiple'>\", \"</select>\" ],\n\n\t// XHTML parsers do not magically insert elements in the\n\t// same way that tag soup parsers do. So we cannot shorten\n\t// this by omitting <tbody> or other required elements.\n\tthead: [ 1, \"<table>\", \"</table>\" ],\n\tcol: [ 2, \"<table><colgroup>\", \"</colgroup></table>\" ],\n\ttr: [ 2, \"<table><tbody>\", \"</tbody></table>\" ],\n\ttd: [ 3, \"<table><tbody><tr>\", \"</tr></tbody></table>\" ],\n\n\t_default: [ 0, \"\", \"\" ]\n};\n\n// Support: IE9\nwrapMap.optgroup = wrapMap.option;\n\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n\nfunction getAll( context, tag ) {\n\n\t// Support: IE9-11+\n\t// Use typeof to avoid zero-argument method invocation on host objects (#15151)\n\tvar ret = typeof context.getElementsByTagName !== \"undefined\" ?\n\t\t\tcontext.getElementsByTagName( tag || \"*\" ) :\n\t\t\ttypeof context.querySelectorAll !== \"undefined\" ?\n\t\t\t\tcontext.querySelectorAll( tag || \"*\" ) :\n\t\t\t[];\n\n\treturn tag === undefined || tag && jQuery.nodeName( context, tag ) ?\n\t\tjQuery.merge( [ context ], ret ) :\n\t\tret;\n}\n\n\n// Mark scripts as having already been evaluated\nfunction setGlobalEval( elems, refElements ) {\n\tvar i = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\tdataPriv.set(\n\t\t\telems[ i ],\n\t\t\t\"globalEval\",\n\t\t\t!refElements || dataPriv.get( refElements[ i ], \"globalEval\" )\n\t\t);\n\t}\n}\n\n\nvar rhtml = /<|&#?\\w+;/;\n\nfunction buildFragment( elems, context, scripts, selection, ignored ) {\n\tvar elem, tmp, tag, wrap, contains, j,\n\t\tfragment = context.createDocumentFragment(),\n\t\tnodes = [],\n\t\ti = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\telem = elems[ i ];\n\n\t\tif ( elem || elem === 0 ) {\n\n\t\t\t// Add nodes directly\n\t\t\tif ( jQuery.type( elem ) === \"object\" ) {\n\n\t\t\t\t// Support: Android<4.1, PhantomJS<2\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );\n\n\t\t\t// Convert non-html into a text node\n\t\t\t} else if ( !rhtml.test( elem ) ) {\n\t\t\t\tnodes.push( context.createTextNode( elem ) );\n\n\t\t\t// Convert html into DOM nodes\n\t\t\t} else {\n\t\t\t\ttmp = tmp || fragment.appendChild( context.createElement( \"div\" ) );\n\n\t\t\t\t// Deserialize a standard representation\n\t\t\t\ttag = ( rtagName.exec( elem ) || [ \"\", \"\" ] )[ 1 ].toLowerCase();\n\t\t\t\twrap = wrapMap[ tag ] || wrapMap._default;\n\t\t\t\ttmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];\n\n\t\t\t\t// Descend through wrappers to the right content\n\t\t\t\tj = wrap[ 0 ];\n\t\t\t\twhile ( j-- ) {\n\t\t\t\t\ttmp = tmp.lastChild;\n\t\t\t\t}\n\n\t\t\t\t// Support: Android<4.1, PhantomJS<2\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, tmp.childNodes );\n\n\t\t\t\t// Remember the top-level container\n\t\t\t\ttmp = fragment.firstChild;\n\n\t\t\t\t// Ensure the created nodes are orphaned (#12392)\n\t\t\t\ttmp.textContent = \"\";\n\t\t\t}\n\t\t}\n\t}\n\n\t// Remove wrapper from fragment\n\tfragment.textContent = \"\";\n\n\ti = 0;\n\twhile ( ( elem = nodes[ i++ ] ) ) {\n\n\t\t// Skip elements already in the context collection (trac-4087)\n\t\tif ( selection && jQuery.inArray( elem, selection ) > -1 ) {\n\t\t\tif ( ignored ) {\n\t\t\t\tignored.push( elem );\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tcontains = jQuery.contains( elem.ownerDocument, elem );\n\n\t\t// Append to fragment\n\t\ttmp = getAll( fragment.appendChild( elem ), \"script\" );\n\n\t\t// Preserve script evaluation history\n\t\tif ( contains ) {\n\t\t\tsetGlobalEval( tmp );\n\t\t}\n\n\t\t// Capture executables\n\t\tif ( scripts ) {\n\t\t\tj = 0;\n\t\t\twhile ( ( elem = tmp[ j++ ] ) ) {\n\t\t\t\tif ( rscriptType.test( elem.type || \"\" ) ) {\n\t\t\t\t\tscripts.push( elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn fragment;\n}\n\n\n( function() {\n\tvar fragment = document.createDocumentFragment(),\n\t\tdiv = fragment.appendChild( document.createElement( \"div\" ) ),\n\t\tinput = document.createElement( \"input\" );\n\n\t// Support: Android 4.0-4.3, Safari<=5.1\n\t// Check state lost if the name is set (#11217)\n\t// Support: Windows Web Apps (WWA)\n\t// `name` and `type` must use .setAttribute for WWA (#14901)\n\tinput.setAttribute( \"type\", \"radio\" );\n\tinput.setAttribute( \"checked\", \"checked\" );\n\tinput.setAttribute( \"name\", \"t\" );\n\n\tdiv.appendChild( input );\n\n\t// Support: Safari<=5.1, Android<4.2\n\t// Older WebKit doesn't clone checked state correctly in fragments\n\tsupport.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;\n\n\t// Support: IE<=11+\n\t// Make sure textarea (and checkbox) defaultValue is properly cloned\n\tdiv.innerHTML = \"<textarea>x</textarea>\";\n\tsupport.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;\n} )();\n\n\nvar\n\trkeyEvent = /^key/,\n\trmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,\n\trtypenamespace = /^([^.]*)(?:\\.(.+)|)/;\n\nfunction returnTrue() {\n\treturn true;\n}\n\nfunction returnFalse() {\n\treturn false;\n}\n\n// Support: IE9\n// See #13393 for more info\nfunction safeActiveElement() {\n\ttry {\n\t\treturn document.activeElement;\n\t} catch ( err ) { }\n}\n\nfunction on( elem, types, selector, data, fn, one ) {\n\tvar origFn, type;\n\n\t// Types can be a map of types/handlers\n\tif ( typeof types === \"object\" ) {\n\n\t\t// ( types-Object, selector, data )\n\t\tif ( typeof selector !== \"string\" ) {\n\n\t\t\t// ( types-Object, data )\n\t\t\tdata = data || selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tfor ( type in types ) {\n\t\t\ton( elem, type, selector, data, types[ type ], one );\n\t\t}\n\t\treturn elem;\n\t}\n\n\tif ( data == null && fn == null ) {\n\n\t\t// ( types, fn )\n\t\tfn = selector;\n\t\tdata = selector = undefined;\n\t} else if ( fn == null ) {\n\t\tif ( typeof selector === \"string\" ) {\n\n\t\t\t// ( types, selector, fn )\n\t\t\tfn = data;\n\t\t\tdata = undefined;\n\t\t} else {\n\n\t\t\t// ( types, data, fn )\n\t\t\tfn = data;\n\t\t\tdata = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t}\n\tif ( fn === false ) {\n\t\tfn = returnFalse;\n\t} else if ( !fn ) {\n\t\treturn this;\n\t}\n\n\tif ( one === 1 ) {\n\t\torigFn = fn;\n\t\tfn = function( event ) {\n\n\t\t\t// Can use an empty set, since event contains the info\n\t\t\tjQuery().off( event );\n\t\t\treturn origFn.apply( this, arguments );\n\t\t};\n\n\t\t// Use same guid so caller can remove using origFn\n\t\tfn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );\n\t}\n\treturn elem.each( function() {\n\t\tjQuery.event.add( this, types, fn, data, selector );\n\t} );\n}\n\n/*\n * Helper functions for managing events -- not part of the public interface.\n * Props to Dean Edwards' addEvent library for many of the ideas.\n */\njQuery.event = {\n\n\tglobal: {},\n\n\tadd: function( elem, types, handler, data, selector ) {\n\n\t\tvar handleObjIn, eventHandle, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.get( elem );\n\n\t\t// Don't attach events to noData or text/comment nodes (but allow plain objects)\n\t\tif ( !elemData ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Caller can pass in an object of custom data in lieu of the handler\n\t\tif ( handler.handler ) {\n\t\t\thandleObjIn = handler;\n\t\t\thandler = handleObjIn.handler;\n\t\t\tselector = handleObjIn.selector;\n\t\t}\n\n\t\t// Make sure that the handler has a unique ID, used to find/remove it later\n\t\tif ( !handler.guid ) {\n\t\t\thandler.guid = jQuery.guid++;\n\t\t}\n\n\t\t// Init the element's event structure and main handler, if this is the first\n\t\tif ( !( events = elemData.events ) ) {\n\t\t\tevents = elemData.events = {};\n\t\t}\n\t\tif ( !( eventHandle = elemData.handle ) ) {\n\t\t\teventHandle = elemData.handle = function( e ) {\n\n\t\t\t\t// Discard the second event of a jQuery.event.trigger() and\n\t\t\t\t// when an event is called after a page has unloaded\n\t\t\t\treturn typeof jQuery !== \"undefined\" && jQuery.event.triggered !== e.type ?\n\t\t\t\t\tjQuery.event.dispatch.apply( elem, arguments ) : undefined;\n\t\t\t};\n\t\t}\n\n\t\t// Handle multiple events separated by a space\n\t\ttypes = ( types || \"\" ).match( rnotwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// There *must* be a type, no attaching namespace-only handlers\n\t\t\tif ( !type ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If event changes its type, use the special event handlers for the changed type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// If selector defined, determine special event api type, otherwise given type\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\n\t\t\t// Update special based on newly reset type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// handleObj is passed to all event handlers\n\t\t\thandleObj = jQuery.extend( {\n\t\t\t\ttype: type,\n\t\t\t\torigType: origType,\n\t\t\t\tdata: data,\n\t\t\t\thandler: handler,\n\t\t\t\tguid: handler.guid,\n\t\t\t\tselector: selector,\n\t\t\t\tneedsContext: selector && jQuery.expr.match.needsContext.test( selector ),\n\t\t\t\tnamespace: namespaces.join( \".\" )\n\t\t\t}, handleObjIn );\n\n\t\t\t// Init the event handler queue if we're the first\n\t\t\tif ( !( handlers = events[ type ] ) ) {\n\t\t\t\thandlers = events[ type ] = [];\n\t\t\t\thandlers.delegateCount = 0;\n\n\t\t\t\t// Only use addEventListener if the special events handler returns false\n\t\t\t\tif ( !special.setup ||\n\t\t\t\t\tspecial.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\n\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\telem.addEventListener( type, eventHandle );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( special.add ) {\n\t\t\t\tspecial.add.call( elem, handleObj );\n\n\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add to the element's handler list, delegates in front\n\t\t\tif ( selector ) {\n\t\t\t\thandlers.splice( handlers.delegateCount++, 0, handleObj );\n\t\t\t} else {\n\t\t\t\thandlers.push( handleObj );\n\t\t\t}\n\n\t\t\t// Keep track of which events have ever been used, for event optimization\n\t\t\tjQuery.event.global[ type ] = true;\n\t\t}\n\n\t},\n\n\t// Detach an event or set of events from an element\n\tremove: function( elem, types, handler, selector, mappedTypes ) {\n\n\t\tvar j, origCount, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.hasData( elem ) && dataPriv.get( elem );\n\n\t\tif ( !elemData || !( events = elemData.events ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Once for each type.namespace in types; type may be omitted\n\t\ttypes = ( types || \"\" ).match( rnotwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// Unbind all events (on this namespace, if provided) for the element\n\t\t\tif ( !type ) {\n\t\t\t\tfor ( type in events ) {\n\t\t\t\t\tjQuery.event.remove( elem, type + types[ t ], handler, selector, true );\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\t\t\thandlers = events[ type ] || [];\n\t\t\ttmp = tmp[ 2 ] &&\n\t\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" );\n\n\t\t\t// Remove matching events\n\t\t\torigCount = j = handlers.length;\n\t\t\twhile ( j-- ) {\n\t\t\t\thandleObj = handlers[ j ];\n\n\t\t\t\tif ( ( mappedTypes || origType === handleObj.origType ) &&\n\t\t\t\t\t( !handler || handler.guid === handleObj.guid ) &&\n\t\t\t\t\t( !tmp || tmp.test( handleObj.namespace ) ) &&\n\t\t\t\t\t( !selector || selector === handleObj.selector ||\n\t\t\t\t\t\tselector === \"**\" && handleObj.selector ) ) {\n\t\t\t\t\thandlers.splice( j, 1 );\n\n\t\t\t\t\tif ( handleObj.selector ) {\n\t\t\t\t\t\thandlers.delegateCount--;\n\t\t\t\t\t}\n\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove generic event handler if we removed something and no more handlers exist\n\t\t\t// (avoids potential for endless recursion during removal of special event handlers)\n\t\t\tif ( origCount && !handlers.length ) {\n\t\t\t\tif ( !special.teardown ||\n\t\t\t\t\tspecial.teardown.call( elem, namespaces, elemData.handle ) === false ) {\n\n\t\t\t\t\tjQuery.removeEvent( elem, type, elemData.handle );\n\t\t\t\t}\n\n\t\t\t\tdelete events[ type ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove data and the expando if it's no longer used\n\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\tdataPriv.remove( elem, \"handle events\" );\n\t\t}\n\t},\n\n\tdispatch: function( event ) {\n\n\t\t// Make a writable jQuery.Event from the native event object\n\t\tevent = jQuery.event.fix( event );\n\n\t\tvar i, j, ret, matched, handleObj,\n\t\t\thandlerQueue = [],\n\t\t\targs = slice.call( arguments ),\n\t\t\thandlers = ( dataPriv.get( this, \"events\" ) || {} )[ event.type ] || [],\n\t\t\tspecial = jQuery.event.special[ event.type ] || {};\n\n\t\t// Use the fix-ed jQuery.Event rather than the (read-only) native event\n\t\targs[ 0 ] = event;\n\t\tevent.delegateTarget = this;\n\n\t\t// Call the preDispatch hook for the mapped type, and let it bail if desired\n\t\tif ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine handlers\n\t\thandlerQueue = jQuery.event.handlers.call( this, event, handlers );\n\n\t\t// Run delegates first; they may want to stop propagation beneath us\n\t\ti = 0;\n\t\twhile ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\tevent.currentTarget = matched.elem;\n\n\t\t\tj = 0;\n\t\t\twhile ( ( handleObj = matched.handlers[ j++ ] ) &&\n\t\t\t\t!event.isImmediatePropagationStopped() ) {\n\n\t\t\t\t// Triggered event must either 1) have no namespace, or 2) have namespace(s)\n\t\t\t\t// a subset or equal to those in the bound event (both can have no namespace).\n\t\t\t\tif ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {\n\n\t\t\t\t\tevent.handleObj = handleObj;\n\t\t\t\t\tevent.data = handleObj.data;\n\n\t\t\t\t\tret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||\n\t\t\t\t\t\thandleObj.handler ).apply( matched.elem, args );\n\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\tif ( ( event.result = ret ) === false ) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Call the postDispatch hook for the mapped type\n\t\tif ( special.postDispatch ) {\n\t\t\tspecial.postDispatch.call( this, event );\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\thandlers: function( event, handlers ) {\n\t\tvar i, matches, sel, handleObj,\n\t\t\thandlerQueue = [],\n\t\t\tdelegateCount = handlers.delegateCount,\n\t\t\tcur = event.target;\n\n\t\t// Support (at least): Chrome, IE9\n\t\t// Find delegate handlers\n\t\t// Black-hole SVG <use> instance trees (#13180)\n\t\t//\n\t\t// Support: Firefox<=42+\n\t\t// Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343)\n\t\tif ( delegateCount && cur.nodeType &&\n\t\t\t( event.type !== \"click\" || isNaN( event.button ) || event.button < 1 ) ) {\n\n\t\t\tfor ( ; cur !== this; cur = cur.parentNode || this ) {\n\n\t\t\t\t// Don't check non-elements (#13208)\n\t\t\t\t// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)\n\t\t\t\tif ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== \"click\" ) ) {\n\t\t\t\t\tmatches = [];\n\t\t\t\t\tfor ( i = 0; i < delegateCount; i++ ) {\n\t\t\t\t\t\thandleObj = handlers[ i ];\n\n\t\t\t\t\t\t// Don't conflict with Object.prototype properties (#13203)\n\t\t\t\t\t\tsel = handleObj.selector + \" \";\n\n\t\t\t\t\t\tif ( matches[ sel ] === undefined ) {\n\t\t\t\t\t\t\tmatches[ sel ] = handleObj.needsContext ?\n\t\t\t\t\t\t\t\tjQuery( sel, this ).index( cur ) > -1 :\n\t\t\t\t\t\t\t\tjQuery.find( sel, this, null, [ cur ] ).length;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( matches[ sel ] ) {\n\t\t\t\t\t\t\tmatches.push( handleObj );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( matches.length ) {\n\t\t\t\t\t\thandlerQueue.push( { elem: cur, handlers: matches } );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add the remaining (directly-bound) handlers\n\t\tif ( delegateCount < handlers.length ) {\n\t\t\thandlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } );\n\t\t}\n\n\t\treturn handlerQueue;\n\t},\n\n\t// Includes some event props shared by KeyEvent and MouseEvent\n\tprops: ( \"altKey bubbles cancelable ctrlKey currentTarget detail eventPhase \" +\n\t\t\"metaKey relatedTarget shiftKey target timeStamp view which\" ).split( \" \" ),\n\n\tfixHooks: {},\n\n\tkeyHooks: {\n\t\tprops: \"char charCode key keyCode\".split( \" \" ),\n\t\tfilter: function( event, original ) {\n\n\t\t\t// Add which for key events\n\t\t\tif ( event.which == null ) {\n\t\t\t\tevent.which = original.charCode != null ? original.charCode : original.keyCode;\n\t\t\t}\n\n\t\t\treturn event;\n\t\t}\n\t},\n\n\tmouseHooks: {\n\t\tprops: ( \"button buttons clientX clientY offsetX offsetY pageX pageY \" +\n\t\t\t\"screenX screenY toElement\" ).split( \" \" ),\n\t\tfilter: function( event, original ) {\n\t\t\tvar eventDoc, doc, body,\n\t\t\t\tbutton = original.button;\n\n\t\t\t// Calculate pageX/Y if missing and clientX/Y available\n\t\t\tif ( event.pageX == null && original.clientX != null ) {\n\t\t\t\teventDoc = event.target.ownerDocument || document;\n\t\t\t\tdoc = eventDoc.documentElement;\n\t\t\t\tbody = eventDoc.body;\n\n\t\t\t\tevent.pageX = original.clientX +\n\t\t\t\t\t( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -\n\t\t\t\t\t( doc && doc.clientLeft || body && body.clientLeft || 0 );\n\t\t\t\tevent.pageY = original.clientY +\n\t\t\t\t\t( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) -\n\t\t\t\t\t( doc && doc.clientTop  || body && body.clientTop  || 0 );\n\t\t\t}\n\n\t\t\t// Add which for click: 1 === left; 2 === middle; 3 === right\n\t\t\t// Note: button is not normalized, so don't use it\n\t\t\tif ( !event.which && button !== undefined ) {\n\t\t\t\tevent.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );\n\t\t\t}\n\n\t\t\treturn event;\n\t\t}\n\t},\n\n\tfix: function( event ) {\n\t\tif ( event[ jQuery.expando ] ) {\n\t\t\treturn event;\n\t\t}\n\n\t\t// Create a writable copy of the event object and normalize some properties\n\t\tvar i, prop, copy,\n\t\t\ttype = event.type,\n\t\t\toriginalEvent = event,\n\t\t\tfixHook = this.fixHooks[ type ];\n\n\t\tif ( !fixHook ) {\n\t\t\tthis.fixHooks[ type ] = fixHook =\n\t\t\t\trmouseEvent.test( type ) ? this.mouseHooks :\n\t\t\t\trkeyEvent.test( type ) ? this.keyHooks :\n\t\t\t\t{};\n\t\t}\n\t\tcopy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;\n\n\t\tevent = new jQuery.Event( originalEvent );\n\n\t\ti = copy.length;\n\t\twhile ( i-- ) {\n\t\t\tprop = copy[ i ];\n\t\t\tevent[ prop ] = originalEvent[ prop ];\n\t\t}\n\n\t\t// Support: Cordova 2.5 (WebKit) (#13255)\n\t\t// All events should have a target; Cordova deviceready doesn't\n\t\tif ( !event.target ) {\n\t\t\tevent.target = document;\n\t\t}\n\n\t\t// Support: Safari 6.0+, Chrome<28\n\t\t// Target should not be a text node (#504, #13143)\n\t\tif ( event.target.nodeType === 3 ) {\n\t\t\tevent.target = event.target.parentNode;\n\t\t}\n\n\t\treturn fixHook.filter ? fixHook.filter( event, originalEvent ) : event;\n\t},\n\n\tspecial: {\n\t\tload: {\n\n\t\t\t// Prevent triggered image.load events from bubbling to window.load\n\t\t\tnoBubble: true\n\t\t},\n\t\tfocus: {\n\n\t\t\t// Fire native event if possible so blur/focus sequence is correct\n\t\t\ttrigger: function() {\n\t\t\t\tif ( this !== safeActiveElement() && this.focus ) {\n\t\t\t\t\tthis.focus();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdelegateType: \"focusin\"\n\t\t},\n\t\tblur: {\n\t\t\ttrigger: function() {\n\t\t\t\tif ( this === safeActiveElement() && this.blur ) {\n\t\t\t\t\tthis.blur();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdelegateType: \"focusout\"\n\t\t},\n\t\tclick: {\n\n\t\t\t// For checkbox, fire native event so checked state will be right\n\t\t\ttrigger: function() {\n\t\t\t\tif ( this.type === \"checkbox\" && this.click && jQuery.nodeName( this, \"input\" ) ) {\n\t\t\t\t\tthis.click();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// For cross-browser consistency, don't fire native .click() on links\n\t\t\t_default: function( event ) {\n\t\t\t\treturn jQuery.nodeName( event.target, \"a\" );\n\t\t\t}\n\t\t},\n\n\t\tbeforeunload: {\n\t\t\tpostDispatch: function( event ) {\n\n\t\t\t\t// Support: Firefox 20+\n\t\t\t\t// Firefox doesn't alert if the returnValue field is not set.\n\t\t\t\tif ( event.result !== undefined && event.originalEvent ) {\n\t\t\t\t\tevent.originalEvent.returnValue = event.result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\njQuery.removeEvent = function( elem, type, handle ) {\n\n\t// This \"if\" is needed for plain objects\n\tif ( elem.removeEventListener ) {\n\t\telem.removeEventListener( type, handle );\n\t}\n};\n\njQuery.Event = function( src, props ) {\n\n\t// Allow instantiation without the 'new' keyword\n\tif ( !( this instanceof jQuery.Event ) ) {\n\t\treturn new jQuery.Event( src, props );\n\t}\n\n\t// Event object\n\tif ( src && src.type ) {\n\t\tthis.originalEvent = src;\n\t\tthis.type = src.type;\n\n\t\t// Events bubbling up the document may have been marked as prevented\n\t\t// by a handler lower down the tree; reflect the correct value.\n\t\tthis.isDefaultPrevented = src.defaultPrevented ||\n\t\t\t\tsrc.defaultPrevented === undefined &&\n\n\t\t\t\t// Support: Android<4.0\n\t\t\t\tsrc.returnValue === false ?\n\t\t\treturnTrue :\n\t\t\treturnFalse;\n\n\t// Event type\n\t} else {\n\t\tthis.type = src;\n\t}\n\n\t// Put explicitly provided properties onto the event object\n\tif ( props ) {\n\t\tjQuery.extend( this, props );\n\t}\n\n\t// Create a timestamp if incoming event doesn't have one\n\tthis.timeStamp = src && src.timeStamp || jQuery.now();\n\n\t// Mark it as fixed\n\tthis[ jQuery.expando ] = true;\n};\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n\tconstructor: jQuery.Event,\n\tisDefaultPrevented: returnFalse,\n\tisPropagationStopped: returnFalse,\n\tisImmediatePropagationStopped: returnFalse,\n\n\tpreventDefault: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isDefaultPrevented = returnTrue;\n\n\t\tif ( e ) {\n\t\t\te.preventDefault();\n\t\t}\n\t},\n\tstopPropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isPropagationStopped = returnTrue;\n\n\t\tif ( e ) {\n\t\t\te.stopPropagation();\n\t\t}\n\t},\n\tstopImmediatePropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isImmediatePropagationStopped = returnTrue;\n\n\t\tif ( e ) {\n\t\t\te.stopImmediatePropagation();\n\t\t}\n\n\t\tthis.stopPropagation();\n\t}\n};\n\n// Create mouseenter/leave events using mouseover/out and event-time checks\n// so that event delegation works in jQuery.\n// Do the same for pointerenter/pointerleave and pointerover/pointerout\n//\n// Support: Safari 7 only\n// Safari sends mouseenter too often; see:\n// https://code.google.com/p/chromium/issues/detail?id=470258\n// for the description of the bug (it existed in older Chrome versions as well).\njQuery.each( {\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\",\n\tpointerenter: \"pointerover\",\n\tpointerleave: \"pointerout\"\n}, function( orig, fix ) {\n\tjQuery.event.special[ orig ] = {\n\t\tdelegateType: fix,\n\t\tbindType: fix,\n\n\t\thandle: function( event ) {\n\t\t\tvar ret,\n\t\t\t\ttarget = this,\n\t\t\t\trelated = event.relatedTarget,\n\t\t\t\thandleObj = event.handleObj;\n\n\t\t\t// For mouseenter/leave call the handler if related is outside the target.\n\t\t\t// NB: No relatedTarget if the mouse left/entered the browser window\n\t\t\tif ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {\n\t\t\t\tevent.type = handleObj.origType;\n\t\t\t\tret = handleObj.handler.apply( this, arguments );\n\t\t\t\tevent.type = fix;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t};\n} );\n\njQuery.fn.extend( {\n\ton: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn );\n\t},\n\tone: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn, 1 );\n\t},\n\toff: function( types, selector, fn ) {\n\t\tvar handleObj, type;\n\t\tif ( types && types.preventDefault && types.handleObj ) {\n\n\t\t\t// ( event )  dispatched jQuery.Event\n\t\t\thandleObj = types.handleObj;\n\t\t\tjQuery( types.delegateTarget ).off(\n\t\t\t\thandleObj.namespace ?\n\t\t\t\t\thandleObj.origType + \".\" + handleObj.namespace :\n\t\t\t\t\thandleObj.origType,\n\t\t\t\thandleObj.selector,\n\t\t\t\thandleObj.handler\n\t\t\t);\n\t\t\treturn this;\n\t\t}\n\t\tif ( typeof types === \"object\" ) {\n\n\t\t\t// ( types-object [, selector] )\n\t\t\tfor ( type in types ) {\n\t\t\t\tthis.off( type, selector, types[ type ] );\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif ( selector === false || typeof selector === \"function\" ) {\n\n\t\t\t// ( types [, fn] )\n\t\t\tfn = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.remove( this, types, fn, selector );\n\t\t} );\n\t}\n} );\n\n\nvar\n\trxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\\w:-]+)[^>]*)\\/>/gi,\n\n\t// Support: IE 10-11, Edge 10240+\n\t// In IE/Edge using regex groups here causes severe slowdowns.\n\t// See https://connect.microsoft.com/IE/feedback/details/1736512/\n\trnoInnerhtml = /<script|<style|<link/i,\n\n\t// checked=\"checked\" or checked\n\trchecked = /checked\\s*(?:[^=]|=\\s*.checked.)/i,\n\trscriptTypeMasked = /^true\\/(.*)/,\n\trcleanScript = /^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g;\n\nfunction manipulationTarget( elem, content ) {\n\tif ( jQuery.nodeName( elem, \"table\" ) &&\n\t\tjQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, \"tr\" ) ) {\n\n\t\treturn elem.getElementsByTagName( \"tbody\" )[ 0 ] || elem;\n\t}\n\n\treturn elem;\n}\n\n// Replace/restore the type attribute of script elements for safe DOM manipulation\nfunction disableScript( elem ) {\n\telem.type = ( elem.getAttribute( \"type\" ) !== null ) + \"/\" + elem.type;\n\treturn elem;\n}\nfunction restoreScript( elem ) {\n\tvar match = rscriptTypeMasked.exec( elem.type );\n\n\tif ( match ) {\n\t\telem.type = match[ 1 ];\n\t} else {\n\t\telem.removeAttribute( \"type\" );\n\t}\n\n\treturn elem;\n}\n\nfunction cloneCopyEvent( src, dest ) {\n\tvar i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;\n\n\tif ( dest.nodeType !== 1 ) {\n\t\treturn;\n\t}\n\n\t// 1. Copy private data: events, handlers, etc.\n\tif ( dataPriv.hasData( src ) ) {\n\t\tpdataOld = dataPriv.access( src );\n\t\tpdataCur = dataPriv.set( dest, pdataOld );\n\t\tevents = pdataOld.events;\n\n\t\tif ( events ) {\n\t\t\tdelete pdataCur.handle;\n\t\t\tpdataCur.events = {};\n\n\t\t\tfor ( type in events ) {\n\t\t\t\tfor ( i = 0, l = events[ type ].length; i < l; i++ ) {\n\t\t\t\t\tjQuery.event.add( dest, type, events[ type ][ i ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2. Copy user data\n\tif ( dataUser.hasData( src ) ) {\n\t\tudataOld = dataUser.access( src );\n\t\tudataCur = jQuery.extend( {}, udataOld );\n\n\t\tdataUser.set( dest, udataCur );\n\t}\n}\n\n// Fix IE bugs, see support tests\nfunction fixInput( src, dest ) {\n\tvar nodeName = dest.nodeName.toLowerCase();\n\n\t// Fails to persist the checked state of a cloned checkbox or radio button.\n\tif ( nodeName === \"input\" && rcheckableType.test( src.type ) ) {\n\t\tdest.checked = src.checked;\n\n\t// Fails to return the selected option to the default selected state when cloning options\n\t} else if ( nodeName === \"input\" || nodeName === \"textarea\" ) {\n\t\tdest.defaultValue = src.defaultValue;\n\t}\n}\n\nfunction domManip( collection, args, callback, ignored ) {\n\n\t// Flatten any nested arrays\n\targs = concat.apply( [], args );\n\n\tvar fragment, first, scripts, hasScripts, node, doc,\n\t\ti = 0,\n\t\tl = collection.length,\n\t\tiNoClone = l - 1,\n\t\tvalue = args[ 0 ],\n\t\tisFunction = jQuery.isFunction( value );\n\n\t// We can't cloneNode fragments that contain checked, in WebKit\n\tif ( isFunction ||\n\t\t\t( l > 1 && typeof value === \"string\" &&\n\t\t\t\t!support.checkClone && rchecked.test( value ) ) ) {\n\t\treturn collection.each( function( index ) {\n\t\t\tvar self = collection.eq( index );\n\t\t\tif ( isFunction ) {\n\t\t\t\targs[ 0 ] = value.call( this, index, self.html() );\n\t\t\t}\n\t\t\tdomManip( self, args, callback, ignored );\n\t\t} );\n\t}\n\n\tif ( l ) {\n\t\tfragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );\n\t\tfirst = fragment.firstChild;\n\n\t\tif ( fragment.childNodes.length === 1 ) {\n\t\t\tfragment = first;\n\t\t}\n\n\t\t// Require either new content or an interest in ignored elements to invoke the callback\n\t\tif ( first || ignored ) {\n\t\t\tscripts = jQuery.map( getAll( fragment, \"script\" ), disableScript );\n\t\t\thasScripts = scripts.length;\n\n\t\t\t// Use the original fragment for the last item\n\t\t\t// instead of the first because it can end up\n\t\t\t// being emptied incorrectly in certain situations (#8070).\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tnode = fragment;\n\n\t\t\t\tif ( i !== iNoClone ) {\n\t\t\t\t\tnode = jQuery.clone( node, true, true );\n\n\t\t\t\t\t// Keep references to cloned scripts for later restoration\n\t\t\t\t\tif ( hasScripts ) {\n\n\t\t\t\t\t\t// Support: Android<4.1, PhantomJS<2\n\t\t\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\t\t\tjQuery.merge( scripts, getAll( node, \"script\" ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcallback.call( collection[ i ], node, i );\n\t\t\t}\n\n\t\t\tif ( hasScripts ) {\n\t\t\t\tdoc = scripts[ scripts.length - 1 ].ownerDocument;\n\n\t\t\t\t// Reenable scripts\n\t\t\t\tjQuery.map( scripts, restoreScript );\n\n\t\t\t\t// Evaluate executable scripts on first document insertion\n\t\t\t\tfor ( i = 0; i < hasScripts; i++ ) {\n\t\t\t\t\tnode = scripts[ i ];\n\t\t\t\t\tif ( rscriptType.test( node.type || \"\" ) &&\n\t\t\t\t\t\t!dataPriv.access( node, \"globalEval\" ) &&\n\t\t\t\t\t\tjQuery.contains( doc, node ) ) {\n\n\t\t\t\t\t\tif ( node.src ) {\n\n\t\t\t\t\t\t\t// Optional AJAX dependency, but won't run scripts if not present\n\t\t\t\t\t\t\tif ( jQuery._evalUrl ) {\n\t\t\t\t\t\t\t\tjQuery._evalUrl( node.src );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tjQuery.globalEval( node.textContent.replace( rcleanScript, \"\" ) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn collection;\n}\n\nfunction remove( elem, selector, keepData ) {\n\tvar node,\n\t\tnodes = selector ? jQuery.filter( selector, elem ) : elem,\n\t\ti = 0;\n\n\tfor ( ; ( node = nodes[ i ] ) != null; i++ ) {\n\t\tif ( !keepData && node.nodeType === 1 ) {\n\t\t\tjQuery.cleanData( getAll( node ) );\n\t\t}\n\n\t\tif ( node.parentNode ) {\n\t\t\tif ( keepData && jQuery.contains( node.ownerDocument, node ) ) {\n\t\t\t\tsetGlobalEval( getAll( node, \"script\" ) );\n\t\t\t}\n\t\t\tnode.parentNode.removeChild( node );\n\t\t}\n\t}\n\n\treturn elem;\n}\n\njQuery.extend( {\n\thtmlPrefilter: function( html ) {\n\t\treturn html.replace( rxhtmlTag, \"<$1></$2>\" );\n\t},\n\n\tclone: function( elem, dataAndEvents, deepDataAndEvents ) {\n\t\tvar i, l, srcElements, destElements,\n\t\t\tclone = elem.cloneNode( true ),\n\t\t\tinPage = jQuery.contains( elem.ownerDocument, elem );\n\n\t\t// Fix IE cloning issues\n\t\tif ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&\n\t\t\t\t!jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2\n\t\t\tdestElements = getAll( clone );\n\t\t\tsrcElements = getAll( elem );\n\n\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\tfixInput( srcElements[ i ], destElements[ i ] );\n\t\t\t}\n\t\t}\n\n\t\t// Copy the events from the original to the clone\n\t\tif ( dataAndEvents ) {\n\t\t\tif ( deepDataAndEvents ) {\n\t\t\t\tsrcElements = srcElements || getAll( elem );\n\t\t\t\tdestElements = destElements || getAll( clone );\n\n\t\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\t\tcloneCopyEvent( srcElements[ i ], destElements[ i ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcloneCopyEvent( elem, clone );\n\t\t\t}\n\t\t}\n\n\t\t// Preserve script evaluation history\n\t\tdestElements = getAll( clone, \"script\" );\n\t\tif ( destElements.length > 0 ) {\n\t\t\tsetGlobalEval( destElements, !inPage && getAll( elem, \"script\" ) );\n\t\t}\n\n\t\t// Return the cloned set\n\t\treturn clone;\n\t},\n\n\tcleanData: function( elems ) {\n\t\tvar data, elem, type,\n\t\t\tspecial = jQuery.event.special,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {\n\t\t\tif ( acceptData( elem ) ) {\n\t\t\t\tif ( ( data = elem[ dataPriv.expando ] ) ) {\n\t\t\t\t\tif ( data.events ) {\n\t\t\t\t\t\tfor ( type in data.events ) {\n\t\t\t\t\t\t\tif ( special[ type ] ) {\n\t\t\t\t\t\t\t\tjQuery.event.remove( elem, type );\n\n\t\t\t\t\t\t\t// This is a shortcut to avoid jQuery.event.remove's overhead\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tjQuery.removeEvent( elem, type, data.handle );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Support: Chrome <= 35-45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataPriv.expando ] = undefined;\n\t\t\t\t}\n\t\t\t\tif ( elem[ dataUser.expando ] ) {\n\n\t\t\t\t\t// Support: Chrome <= 35-45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataUser.expando ] = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n} );\n\njQuery.fn.extend( {\n\n\t// Keep domManip exposed until 3.0 (gh-2225)\n\tdomManip: domManip,\n\n\tdetach: function( selector ) {\n\t\treturn remove( this, selector, true );\n\t},\n\n\tremove: function( selector ) {\n\t\treturn remove( this, selector );\n\t},\n\n\ttext: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\treturn value === undefined ?\n\t\t\t\tjQuery.text( this ) :\n\t\t\t\tthis.empty().each( function() {\n\t\t\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\t\t\tthis.textContent = value;\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t}, null, value, arguments.length );\n\t},\n\n\tappend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.appendChild( elem );\n\t\t\t}\n\t\t} );\n\t},\n\n\tprepend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.insertBefore( elem, target.firstChild );\n\t\t\t}\n\t\t} );\n\t},\n\n\tbefore: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this );\n\t\t\t}\n\t\t} );\n\t},\n\n\tafter: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this.nextSibling );\n\t\t\t}\n\t\t} );\n\t},\n\n\tempty: function() {\n\t\tvar elem,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = this[ i ] ) != null; i++ ) {\n\t\t\tif ( elem.nodeType === 1 ) {\n\n\t\t\t\t// Prevent memory leaks\n\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\n\t\t\t\t// Remove any remaining nodes\n\t\t\t\telem.textContent = \"\";\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tclone: function( dataAndEvents, deepDataAndEvents ) {\n\t\tdataAndEvents = dataAndEvents == null ? false : dataAndEvents;\n\t\tdeepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;\n\n\t\treturn this.map( function() {\n\t\t\treturn jQuery.clone( this, dataAndEvents, deepDataAndEvents );\n\t\t} );\n\t},\n\n\thtml: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\tvar elem = this[ 0 ] || {},\n\t\t\t\ti = 0,\n\t\t\t\tl = this.length;\n\n\t\t\tif ( value === undefined && elem.nodeType === 1 ) {\n\t\t\t\treturn elem.innerHTML;\n\t\t\t}\n\n\t\t\t// See if we can take a shortcut and just use innerHTML\n\t\t\tif ( typeof value === \"string\" && !rnoInnerhtml.test( value ) &&\n\t\t\t\t!wrapMap[ ( rtagName.exec( value ) || [ \"\", \"\" ] )[ 1 ].toLowerCase() ] ) {\n\n\t\t\t\tvalue = jQuery.htmlPrefilter( value );\n\n\t\t\t\ttry {\n\t\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\t\telem = this[ i ] || {};\n\n\t\t\t\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\t\t\t\t\t\t\telem.innerHTML = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\telem = 0;\n\n\t\t\t\t// If using innerHTML throws an exception, use the fallback method\n\t\t\t\t} catch ( e ) {}\n\t\t\t}\n\n\t\t\tif ( elem ) {\n\t\t\t\tthis.empty().append( value );\n\t\t\t}\n\t\t}, null, value, arguments.length );\n\t},\n\n\treplaceWith: function() {\n\t\tvar ignored = [];\n\n\t\t// Make the changes, replacing each non-ignored context element with the new content\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tvar parent = this.parentNode;\n\n\t\t\tif ( jQuery.inArray( this, ignored ) < 0 ) {\n\t\t\t\tjQuery.cleanData( getAll( this ) );\n\t\t\t\tif ( parent ) {\n\t\t\t\t\tparent.replaceChild( elem, this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Force callback invocation\n\t\t}, ignored );\n\t}\n} );\n\njQuery.each( {\n\tappendTo: \"append\",\n\tprependTo: \"prepend\",\n\tinsertBefore: \"before\",\n\tinsertAfter: \"after\",\n\treplaceAll: \"replaceWith\"\n}, function( name, original ) {\n\tjQuery.fn[ name ] = function( selector ) {\n\t\tvar elems,\n\t\t\tret = [],\n\t\t\tinsert = jQuery( selector ),\n\t\t\tlast = insert.length - 1,\n\t\t\ti = 0;\n\n\t\tfor ( ; i <= last; i++ ) {\n\t\t\telems = i === last ? this : this.clone( true );\n\t\t\tjQuery( insert[ i ] )[ original ]( elems );\n\n\t\t\t// Support: QtWebKit\n\t\t\t// .get() because push.apply(_, arraylike) throws\n\t\t\tpush.apply( ret, elems.get() );\n\t\t}\n\n\t\treturn this.pushStack( ret );\n\t};\n} );\n\n\nvar iframe,\n\telemdisplay = {\n\n\t\t// Support: Firefox\n\t\t// We have to pre-define these values for FF (#10227)\n\t\tHTML: \"block\",\n\t\tBODY: \"block\"\n\t};\n\n/**\n * Retrieve the actual display of a element\n * @param {String} name nodeName of the element\n * @param {Object} doc Document object\n */\n\n// Called only from within defaultDisplay\nfunction actualDisplay( name, doc ) {\n\tvar elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),\n\n\t\tdisplay = jQuery.css( elem[ 0 ], \"display\" );\n\n\t// We don't have any data stored on the element,\n\t// so use \"detach\" method as fast way to get rid of the element\n\telem.detach();\n\n\treturn display;\n}\n\n/**\n * Try to determine the default display value of an element\n * @param {String} nodeName\n */\nfunction defaultDisplay( nodeName ) {\n\tvar doc = document,\n\t\tdisplay = elemdisplay[ nodeName ];\n\n\tif ( !display ) {\n\t\tdisplay = actualDisplay( nodeName, doc );\n\n\t\t// If the simple way fails, read from inside an iframe\n\t\tif ( display === \"none\" || !display ) {\n\n\t\t\t// Use the already-created iframe if possible\n\t\t\tiframe = ( iframe || jQuery( \"<iframe frameborder='0' width='0' height='0'/>\" ) )\n\t\t\t\t.appendTo( doc.documentElement );\n\n\t\t\t// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse\n\t\t\tdoc = iframe[ 0 ].contentDocument;\n\n\t\t\t// Support: IE\n\t\t\tdoc.write();\n\t\t\tdoc.close();\n\n\t\t\tdisplay = actualDisplay( nodeName, doc );\n\t\t\tiframe.detach();\n\t\t}\n\n\t\t// Store the correct default display\n\t\telemdisplay[ nodeName ] = display;\n\t}\n\n\treturn display;\n}\nvar rmargin = ( /^margin/ );\n\nvar rnumnonpx = new RegExp( \"^(\" + pnum + \")(?!px)[a-z%]+$\", \"i\" );\n\nvar getStyles = function( elem ) {\n\n\t\t// Support: IE<=11+, Firefox<=30+ (#15098, #14150)\n\t\t// IE throws on elements created in popups\n\t\t// FF meanwhile throws on frame elements through \"defaultView.getComputedStyle\"\n\t\tvar view = elem.ownerDocument.defaultView;\n\n\t\tif ( !view.opener ) {\n\t\t\tview = window;\n\t\t}\n\n\t\treturn view.getComputedStyle( elem );\n\t};\n\nvar swap = function( elem, options, callback, args ) {\n\tvar ret, name,\n\t\told = {};\n\n\t// Remember the old values, and insert the new ones\n\tfor ( name in options ) {\n\t\told[ name ] = elem.style[ name ];\n\t\telem.style[ name ] = options[ name ];\n\t}\n\n\tret = callback.apply( elem, args || [] );\n\n\t// Revert the old values\n\tfor ( name in options ) {\n\t\telem.style[ name ] = old[ name ];\n\t}\n\n\treturn ret;\n};\n\n\nvar documentElement = document.documentElement;\n\n\n\n( function() {\n\tvar pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal,\n\t\tcontainer = document.createElement( \"div\" ),\n\t\tdiv = document.createElement( \"div\" );\n\n\t// Finish early in limited (non-browser) environments\n\tif ( !div.style ) {\n\t\treturn;\n\t}\n\n\t// Support: IE9-11+\n\t// Style of cloned element affects source element cloned (#8908)\n\tdiv.style.backgroundClip = \"content-box\";\n\tdiv.cloneNode( true ).style.backgroundClip = \"\";\n\tsupport.clearCloneStyle = div.style.backgroundClip === \"content-box\";\n\n\tcontainer.style.cssText = \"border:0;width:8px;height:0;top:0;left:-9999px;\" +\n\t\t\"padding:0;margin-top:1px;position:absolute\";\n\tcontainer.appendChild( div );\n\n\t// Executing both pixelPosition & boxSizingReliable tests require only one layout\n\t// so they're executed at the same time to save the second computation.\n\tfunction computeStyleTests() {\n\t\tdiv.style.cssText =\n\n\t\t\t// Support: Firefox<29, Android 2.3\n\t\t\t// Vendor-prefix box-sizing\n\t\t\t\"-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;\" +\n\t\t\t\"position:relative;display:block;\" +\n\t\t\t\"margin:auto;border:1px;padding:1px;\" +\n\t\t\t\"top:1%;width:50%\";\n\t\tdiv.innerHTML = \"\";\n\t\tdocumentElement.appendChild( container );\n\n\t\tvar divStyle = window.getComputedStyle( div );\n\t\tpixelPositionVal = divStyle.top !== \"1%\";\n\t\treliableMarginLeftVal = divStyle.marginLeft === \"2px\";\n\t\tboxSizingReliableVal = divStyle.width === \"4px\";\n\n\t\t// Support: Android 4.0 - 4.3 only\n\t\t// Some styles come back with percentage values, even though they shouldn't\n\t\tdiv.style.marginRight = \"50%\";\n\t\tpixelMarginRightVal = divStyle.marginRight === \"4px\";\n\n\t\tdocumentElement.removeChild( container );\n\t}\n\n\tjQuery.extend( support, {\n\t\tpixelPosition: function() {\n\n\t\t\t// This test is executed only once but we still do memoizing\n\t\t\t// since we can use the boxSizingReliable pre-computing.\n\t\t\t// No need to check if the test was already performed, though.\n\t\t\tcomputeStyleTests();\n\t\t\treturn pixelPositionVal;\n\t\t},\n\t\tboxSizingReliable: function() {\n\t\t\tif ( boxSizingReliableVal == null ) {\n\t\t\t\tcomputeStyleTests();\n\t\t\t}\n\t\t\treturn boxSizingReliableVal;\n\t\t},\n\t\tpixelMarginRight: function() {\n\n\t\t\t// Support: Android 4.0-4.3\n\t\t\t// We're checking for boxSizingReliableVal here instead of pixelMarginRightVal\n\t\t\t// since that compresses better and they're computed together anyway.\n\t\t\tif ( boxSizingReliableVal == null ) {\n\t\t\t\tcomputeStyleTests();\n\t\t\t}\n\t\t\treturn pixelMarginRightVal;\n\t\t},\n\t\treliableMarginLeft: function() {\n\n\t\t\t// Support: IE <=8 only, Android 4.0 - 4.3 only, Firefox <=3 - 37\n\t\t\tif ( boxSizingReliableVal == null ) {\n\t\t\t\tcomputeStyleTests();\n\t\t\t}\n\t\t\treturn reliableMarginLeftVal;\n\t\t},\n\t\treliableMarginRight: function() {\n\n\t\t\t// Support: Android 2.3\n\t\t\t// Check if div with explicit width and no margin-right incorrectly\n\t\t\t// gets computed margin-right based on width of container. (#3333)\n\t\t\t// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right\n\t\t\t// This support function is only executed once so no memoizing is needed.\n\t\t\tvar ret,\n\t\t\t\tmarginDiv = div.appendChild( document.createElement( \"div\" ) );\n\n\t\t\t// Reset CSS: box-sizing; display; margin; border; padding\n\t\t\tmarginDiv.style.cssText = div.style.cssText =\n\n\t\t\t\t// Support: Android 2.3\n\t\t\t\t// Vendor-prefix box-sizing\n\t\t\t\t\"-webkit-box-sizing:content-box;box-sizing:content-box;\" +\n\t\t\t\t\"display:block;margin:0;border:0;padding:0\";\n\t\t\tmarginDiv.style.marginRight = marginDiv.style.width = \"0\";\n\t\t\tdiv.style.width = \"1px\";\n\t\t\tdocumentElement.appendChild( container );\n\n\t\t\tret = !parseFloat( window.getComputedStyle( marginDiv ).marginRight );\n\n\t\t\tdocumentElement.removeChild( container );\n\t\t\tdiv.removeChild( marginDiv );\n\n\t\t\treturn ret;\n\t\t}\n\t} );\n} )();\n\n\nfunction curCSS( elem, name, computed ) {\n\tvar width, minWidth, maxWidth, ret,\n\t\tstyle = elem.style;\n\n\tcomputed = computed || getStyles( elem );\n\n\t// Support: IE9\n\t// getPropertyValue is only needed for .css('filter') (#12537)\n\tif ( computed ) {\n\t\tret = computed.getPropertyValue( name ) || computed[ name ];\n\n\t\tif ( ret === \"\" && !jQuery.contains( elem.ownerDocument, elem ) ) {\n\t\t\tret = jQuery.style( elem, name );\n\t\t}\n\n\t\t// A tribute to the \"awesome hack by Dean Edwards\"\n\t\t// Android Browser returns percentage for some values,\n\t\t// but width seems to be reliably pixels.\n\t\t// This is against the CSSOM draft spec:\n\t\t// http://dev.w3.org/csswg/cssom/#resolved-values\n\t\tif ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) {\n\n\t\t\t// Remember the original values\n\t\t\twidth = style.width;\n\t\t\tminWidth = style.minWidth;\n\t\t\tmaxWidth = style.maxWidth;\n\n\t\t\t// Put in the new values to get a computed value out\n\t\t\tstyle.minWidth = style.maxWidth = style.width = ret;\n\t\t\tret = computed.width;\n\n\t\t\t// Revert the changed values\n\t\t\tstyle.width = width;\n\t\t\tstyle.minWidth = minWidth;\n\t\t\tstyle.maxWidth = maxWidth;\n\t\t}\n\t}\n\n\treturn ret !== undefined ?\n\n\t\t// Support: IE9-11+\n\t\t// IE returns zIndex value as an integer.\n\t\tret + \"\" :\n\t\tret;\n}\n\n\nfunction addGetHookIf( conditionFn, hookFn ) {\n\n\t// Define the hook, we'll check on the first run if it's really needed.\n\treturn {\n\t\tget: function() {\n\t\t\tif ( conditionFn() ) {\n\n\t\t\t\t// Hook not needed (or it's not possible to use it due\n\t\t\t\t// to missing dependency), remove it.\n\t\t\t\tdelete this.get;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Hook needed; redefine it so that the support test is not executed again.\n\t\t\treturn ( this.get = hookFn ).apply( this, arguments );\n\t\t}\n\t};\n}\n\n\nvar\n\n\t// Swappable if display is none or starts with table\n\t// except \"table\", \"table-cell\", or \"table-caption\"\n\t// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display\n\trdisplayswap = /^(none|table(?!-c[ea]).+)/,\n\n\tcssShow = { position: \"absolute\", visibility: \"hidden\", display: \"block\" },\n\tcssNormalTransform = {\n\t\tletterSpacing: \"0\",\n\t\tfontWeight: \"400\"\n\t},\n\n\tcssPrefixes = [ \"Webkit\", \"O\", \"Moz\", \"ms\" ],\n\temptyStyle = document.createElement( \"div\" ).style;\n\n// Return a css property mapped to a potentially vendor prefixed property\nfunction vendorPropName( name ) {\n\n\t// Shortcut for names that are not vendor prefixed\n\tif ( name in emptyStyle ) {\n\t\treturn name;\n\t}\n\n\t// Check for vendor prefixed names\n\tvar capName = name[ 0 ].toUpperCase() + name.slice( 1 ),\n\t\ti = cssPrefixes.length;\n\n\twhile ( i-- ) {\n\t\tname = cssPrefixes[ i ] + capName;\n\t\tif ( name in emptyStyle ) {\n\t\t\treturn name;\n\t\t}\n\t}\n}\n\nfunction setPositiveNumber( elem, value, subtract ) {\n\n\t// Any relative (+/-) values have already been\n\t// normalized at this point\n\tvar matches = rcssNum.exec( value );\n\treturn matches ?\n\n\t\t// Guard against undefined \"subtract\", e.g., when used as in cssHooks\n\t\tMath.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || \"px\" ) :\n\t\tvalue;\n}\n\nfunction augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {\n\tvar i = extra === ( isBorderBox ? \"border\" : \"content\" ) ?\n\n\t\t// If we already have the right measurement, avoid augmentation\n\t\t4 :\n\n\t\t// Otherwise initialize for horizontal or vertical properties\n\t\tname === \"width\" ? 1 : 0,\n\n\t\tval = 0;\n\n\tfor ( ; i < 4; i += 2 ) {\n\n\t\t// Both box models exclude margin, so add it if we want it\n\t\tif ( extra === \"margin\" ) {\n\t\t\tval += jQuery.css( elem, extra + cssExpand[ i ], true, styles );\n\t\t}\n\n\t\tif ( isBorderBox ) {\n\n\t\t\t// border-box includes padding, so remove it if we want content\n\t\t\tif ( extra === \"content\" ) {\n\t\t\t\tval -= jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\t\t\t}\n\n\t\t\t// At this point, extra isn't border nor margin, so remove border\n\t\t\tif ( extra !== \"margin\" ) {\n\t\t\t\tval -= jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\t\t} else {\n\n\t\t\t// At this point, extra isn't content, so add padding\n\t\t\tval += jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\n\t\t\t// At this point, extra isn't content nor padding, so add border\n\t\t\tif ( extra !== \"padding\" ) {\n\t\t\t\tval += jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\t\t}\n\t}\n\n\treturn val;\n}\n\nfunction getWidthOrHeight( elem, name, extra ) {\n\n\t// Start with offset property, which is equivalent to the border-box value\n\tvar valueIsBorderBox = true,\n\t\tval = name === \"width\" ? elem.offsetWidth : elem.offsetHeight,\n\t\tstyles = getStyles( elem ),\n\t\tisBorderBox = jQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\";\n\n\t// Support: IE11 only\n\t// In IE 11 fullscreen elements inside of an iframe have\n\t// 100x too small dimensions (gh-1764).\n\tif ( document.msFullscreenElement && window.top !== window ) {\n\n\t\t// Support: IE11 only\n\t\t// Running getBoundingClientRect on a disconnected node\n\t\t// in IE throws an error.\n\t\tif ( elem.getClientRects().length ) {\n\t\t\tval = Math.round( elem.getBoundingClientRect()[ name ] * 100 );\n\t\t}\n\t}\n\n\t// Some non-html elements return undefined for offsetWidth, so check for null/undefined\n\t// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285\n\t// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668\n\tif ( val <= 0 || val == null ) {\n\n\t\t// Fall back to computed then uncomputed css if necessary\n\t\tval = curCSS( elem, name, styles );\n\t\tif ( val < 0 || val == null ) {\n\t\t\tval = elem.style[ name ];\n\t\t}\n\n\t\t// Computed unit is not pixels. Stop here and return.\n\t\tif ( rnumnonpx.test( val ) ) {\n\t\t\treturn val;\n\t\t}\n\n\t\t// Check for style in case a browser which returns unreliable values\n\t\t// for getComputedStyle silently falls back to the reliable elem.style\n\t\tvalueIsBorderBox = isBorderBox &&\n\t\t\t( support.boxSizingReliable() || val === elem.style[ name ] );\n\n\t\t// Normalize \"\", auto, and prepare for extra\n\t\tval = parseFloat( val ) || 0;\n\t}\n\n\t// Use the active box-sizing model to add/subtract irrelevant styles\n\treturn ( val +\n\t\taugmentWidthOrHeight(\n\t\t\telem,\n\t\t\tname,\n\t\t\textra || ( isBorderBox ? \"border\" : \"content\" ),\n\t\t\tvalueIsBorderBox,\n\t\t\tstyles\n\t\t)\n\t) + \"px\";\n}\n\nfunction showHide( elements, show ) {\n\tvar display, elem, hidden,\n\t\tvalues = [],\n\t\tindex = 0,\n\t\tlength = elements.length;\n\n\tfor ( ; index < length; index++ ) {\n\t\telem = elements[ index ];\n\t\tif ( !elem.style ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tvalues[ index ] = dataPriv.get( elem, \"olddisplay\" );\n\t\tdisplay = elem.style.display;\n\t\tif ( show ) {\n\n\t\t\t// Reset the inline display of this element to learn if it is\n\t\t\t// being hidden by cascaded rules or not\n\t\t\tif ( !values[ index ] && display === \"none\" ) {\n\t\t\t\telem.style.display = \"\";\n\t\t\t}\n\n\t\t\t// Set elements which have been overridden with display: none\n\t\t\t// in a stylesheet to whatever the default browser style is\n\t\t\t// for such an element\n\t\t\tif ( elem.style.display === \"\" && isHidden( elem ) ) {\n\t\t\t\tvalues[ index ] = dataPriv.access(\n\t\t\t\t\telem,\n\t\t\t\t\t\"olddisplay\",\n\t\t\t\t\tdefaultDisplay( elem.nodeName )\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\thidden = isHidden( elem );\n\n\t\t\tif ( display !== \"none\" || !hidden ) {\n\t\t\t\tdataPriv.set(\n\t\t\t\t\telem,\n\t\t\t\t\t\"olddisplay\",\n\t\t\t\t\thidden ? display : jQuery.css( elem, \"display\" )\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Set the display of most of the elements in a second loop\n\t// to avoid the constant reflow\n\tfor ( index = 0; index < length; index++ ) {\n\t\telem = elements[ index ];\n\t\tif ( !elem.style ) {\n\t\t\tcontinue;\n\t\t}\n\t\tif ( !show || elem.style.display === \"none\" || elem.style.display === \"\" ) {\n\t\t\telem.style.display = show ? values[ index ] || \"\" : \"none\";\n\t\t}\n\t}\n\n\treturn elements;\n}\n\njQuery.extend( {\n\n\t// Add in style property hooks for overriding the default\n\t// behavior of getting and setting a style property\n\tcssHooks: {\n\t\topacity: {\n\t\t\tget: function( elem, computed ) {\n\t\t\t\tif ( computed ) {\n\n\t\t\t\t\t// We should always get a number back from opacity\n\t\t\t\t\tvar ret = curCSS( elem, \"opacity\" );\n\t\t\t\t\treturn ret === \"\" ? \"1\" : ret;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t// Don't automatically add \"px\" to these possibly-unitless properties\n\tcssNumber: {\n\t\t\"animationIterationCount\": true,\n\t\t\"columnCount\": true,\n\t\t\"fillOpacity\": true,\n\t\t\"flexGrow\": true,\n\t\t\"flexShrink\": true,\n\t\t\"fontWeight\": true,\n\t\t\"lineHeight\": true,\n\t\t\"opacity\": true,\n\t\t\"order\": true,\n\t\t\"orphans\": true,\n\t\t\"widows\": true,\n\t\t\"zIndex\": true,\n\t\t\"zoom\": true\n\t},\n\n\t// Add in properties whose names you wish to fix before\n\t// setting or getting the value\n\tcssProps: {\n\t\t\"float\": \"cssFloat\"\n\t},\n\n\t// Get and set the style property on a DOM Node\n\tstyle: function( elem, name, value, extra ) {\n\n\t\t// Don't set styles on text and comment nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure that we're working with the right name\n\t\tvar ret, type, hooks,\n\t\t\torigName = jQuery.camelCase( name ),\n\t\t\tstyle = elem.style;\n\n\t\tname = jQuery.cssProps[ origName ] ||\n\t\t\t( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );\n\n\t\t// Gets hook for the prefixed version, then unprefixed version\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// Check if we're setting a value\n\t\tif ( value !== undefined ) {\n\t\t\ttype = typeof value;\n\n\t\t\t// Convert \"+=\" or \"-=\" to relative numbers (#7345)\n\t\t\tif ( type === \"string\" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {\n\t\t\t\tvalue = adjustCSS( elem, name, ret );\n\n\t\t\t\t// Fixes bug #9237\n\t\t\t\ttype = \"number\";\n\t\t\t}\n\n\t\t\t// Make sure that null and NaN values aren't set (#7116)\n\t\t\tif ( value == null || value !== value ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If a number was passed in, add the unit (except for certain CSS properties)\n\t\t\tif ( type === \"number\" ) {\n\t\t\t\tvalue += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? \"\" : \"px\" );\n\t\t\t}\n\n\t\t\t// Support: IE9-11+\n\t\t\t// background-* props affect original clone's values\n\t\t\tif ( !support.clearCloneStyle && value === \"\" && name.indexOf( \"background\" ) === 0 ) {\n\t\t\t\tstyle[ name ] = \"inherit\";\n\t\t\t}\n\n\t\t\t// If a hook was provided, use that value, otherwise just set the specified value\n\t\t\tif ( !hooks || !( \"set\" in hooks ) ||\n\t\t\t\t( value = hooks.set( elem, value, extra ) ) !== undefined ) {\n\n\t\t\t\tstyle[ name ] = value;\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// If a hook was provided get the non-computed value from there\n\t\t\tif ( hooks && \"get\" in hooks &&\n\t\t\t\t( ret = hooks.get( elem, false, extra ) ) !== undefined ) {\n\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\t// Otherwise just get the value from the style object\n\t\t\treturn style[ name ];\n\t\t}\n\t},\n\n\tcss: function( elem, name, extra, styles ) {\n\t\tvar val, num, hooks,\n\t\t\torigName = jQuery.camelCase( name );\n\n\t\t// Make sure that we're working with the right name\n\t\tname = jQuery.cssProps[ origName ] ||\n\t\t\t( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );\n\n\t\t// Try prefixed name followed by the unprefixed name\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// If a hook was provided get the computed value from there\n\t\tif ( hooks && \"get\" in hooks ) {\n\t\t\tval = hooks.get( elem, true, extra );\n\t\t}\n\n\t\t// Otherwise, if a way to get the computed value exists, use that\n\t\tif ( val === undefined ) {\n\t\t\tval = curCSS( elem, name, styles );\n\t\t}\n\n\t\t// Convert \"normal\" to computed value\n\t\tif ( val === \"normal\" && name in cssNormalTransform ) {\n\t\t\tval = cssNormalTransform[ name ];\n\t\t}\n\n\t\t// Make numeric if forced or a qualifier was provided and val looks numeric\n\t\tif ( extra === \"\" || extra ) {\n\t\t\tnum = parseFloat( val );\n\t\t\treturn extra === true || isFinite( num ) ? num || 0 : val;\n\t\t}\n\t\treturn val;\n\t}\n} );\n\njQuery.each( [ \"height\", \"width\" ], function( i, name ) {\n\tjQuery.cssHooks[ name ] = {\n\t\tget: function( elem, computed, extra ) {\n\t\t\tif ( computed ) {\n\n\t\t\t\t// Certain elements can have dimension info if we invisibly show them\n\t\t\t\t// but it must have a current display style that would benefit\n\t\t\t\treturn rdisplayswap.test( jQuery.css( elem, \"display\" ) ) &&\n\t\t\t\t\telem.offsetWidth === 0 ?\n\t\t\t\t\t\tswap( elem, cssShow, function() {\n\t\t\t\t\t\t\treturn getWidthOrHeight( elem, name, extra );\n\t\t\t\t\t\t} ) :\n\t\t\t\t\t\tgetWidthOrHeight( elem, name, extra );\n\t\t\t}\n\t\t},\n\n\t\tset: function( elem, value, extra ) {\n\t\t\tvar matches,\n\t\t\t\tstyles = extra && getStyles( elem ),\n\t\t\t\tsubtract = extra && augmentWidthOrHeight(\n\t\t\t\t\telem,\n\t\t\t\t\tname,\n\t\t\t\t\textra,\n\t\t\t\t\tjQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\",\n\t\t\t\t\tstyles\n\t\t\t\t);\n\n\t\t\t// Convert to pixels if value adjustment is needed\n\t\t\tif ( subtract && ( matches = rcssNum.exec( value ) ) &&\n\t\t\t\t( matches[ 3 ] || \"px\" ) !== \"px\" ) {\n\n\t\t\t\telem.style[ name ] = value;\n\t\t\t\tvalue = jQuery.css( elem, name );\n\t\t\t}\n\n\t\t\treturn setPositiveNumber( elem, value, subtract );\n\t\t}\n\t};\n} );\n\njQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,\n\tfunction( elem, computed ) {\n\t\tif ( computed ) {\n\t\t\treturn ( parseFloat( curCSS( elem, \"marginLeft\" ) ) ||\n\t\t\t\telem.getBoundingClientRect().left -\n\t\t\t\t\tswap( elem, { marginLeft: 0 }, function() {\n\t\t\t\t\t\treturn elem.getBoundingClientRect().left;\n\t\t\t\t\t} )\n\t\t\t\t) + \"px\";\n\t\t}\n\t}\n);\n\n// Support: Android 2.3\njQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,\n\tfunction( elem, computed ) {\n\t\tif ( computed ) {\n\t\t\treturn swap( elem, { \"display\": \"inline-block\" },\n\t\t\t\tcurCSS, [ elem, \"marginRight\" ] );\n\t\t}\n\t}\n);\n\n// These hooks are used by animate to expand properties\njQuery.each( {\n\tmargin: \"\",\n\tpadding: \"\",\n\tborder: \"Width\"\n}, function( prefix, suffix ) {\n\tjQuery.cssHooks[ prefix + suffix ] = {\n\t\texpand: function( value ) {\n\t\t\tvar i = 0,\n\t\t\t\texpanded = {},\n\n\t\t\t\t// Assumes a single number if not a string\n\t\t\t\tparts = typeof value === \"string\" ? value.split( \" \" ) : [ value ];\n\n\t\t\tfor ( ; i < 4; i++ ) {\n\t\t\t\texpanded[ prefix + cssExpand[ i ] + suffix ] =\n\t\t\t\t\tparts[ i ] || parts[ i - 2 ] || parts[ 0 ];\n\t\t\t}\n\n\t\t\treturn expanded;\n\t\t}\n\t};\n\n\tif ( !rmargin.test( prefix ) ) {\n\t\tjQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;\n\t}\n} );\n\njQuery.fn.extend( {\n\tcss: function( name, value ) {\n\t\treturn access( this, function( elem, name, value ) {\n\t\t\tvar styles, len,\n\t\t\t\tmap = {},\n\t\t\t\ti = 0;\n\n\t\t\tif ( jQuery.isArray( name ) ) {\n\t\t\t\tstyles = getStyles( elem );\n\t\t\t\tlen = name.length;\n\n\t\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\t\tmap[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );\n\t\t\t\t}\n\n\t\t\t\treturn map;\n\t\t\t}\n\n\t\t\treturn value !== undefined ?\n\t\t\t\tjQuery.style( elem, name, value ) :\n\t\t\t\tjQuery.css( elem, name );\n\t\t}, name, value, arguments.length > 1 );\n\t},\n\tshow: function() {\n\t\treturn showHide( this, true );\n\t},\n\thide: function() {\n\t\treturn showHide( this );\n\t},\n\ttoggle: function( state ) {\n\t\tif ( typeof state === \"boolean\" ) {\n\t\t\treturn state ? this.show() : this.hide();\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tif ( isHidden( this ) ) {\n\t\t\t\tjQuery( this ).show();\n\t\t\t} else {\n\t\t\t\tjQuery( this ).hide();\n\t\t\t}\n\t\t} );\n\t}\n} );\n\n\nfunction Tween( elem, options, prop, end, easing ) {\n\treturn new Tween.prototype.init( elem, options, prop, end, easing );\n}\njQuery.Tween = Tween;\n\nTween.prototype = {\n\tconstructor: Tween,\n\tinit: function( elem, options, prop, end, easing, unit ) {\n\t\tthis.elem = elem;\n\t\tthis.prop = prop;\n\t\tthis.easing = easing || jQuery.easing._default;\n\t\tthis.options = options;\n\t\tthis.start = this.now = this.cur();\n\t\tthis.end = end;\n\t\tthis.unit = unit || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" );\n\t},\n\tcur: function() {\n\t\tvar hooks = Tween.propHooks[ this.prop ];\n\n\t\treturn hooks && hooks.get ?\n\t\t\thooks.get( this ) :\n\t\t\tTween.propHooks._default.get( this );\n\t},\n\trun: function( percent ) {\n\t\tvar eased,\n\t\t\thooks = Tween.propHooks[ this.prop ];\n\n\t\tif ( this.options.duration ) {\n\t\t\tthis.pos = eased = jQuery.easing[ this.easing ](\n\t\t\t\tpercent, this.options.duration * percent, 0, 1, this.options.duration\n\t\t\t);\n\t\t} else {\n\t\t\tthis.pos = eased = percent;\n\t\t}\n\t\tthis.now = ( this.end - this.start ) * eased + this.start;\n\n\t\tif ( this.options.step ) {\n\t\t\tthis.options.step.call( this.elem, this.now, this );\n\t\t}\n\n\t\tif ( hooks && hooks.set ) {\n\t\t\thooks.set( this );\n\t\t} else {\n\t\t\tTween.propHooks._default.set( this );\n\t\t}\n\t\treturn this;\n\t}\n};\n\nTween.prototype.init.prototype = Tween.prototype;\n\nTween.propHooks = {\n\t_default: {\n\t\tget: function( tween ) {\n\t\t\tvar result;\n\n\t\t\t// Use a property on the element directly when it is not a DOM element,\n\t\t\t// or when there is no matching style property that exists.\n\t\t\tif ( tween.elem.nodeType !== 1 ||\n\t\t\t\ttween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {\n\t\t\t\treturn tween.elem[ tween.prop ];\n\t\t\t}\n\n\t\t\t// Passing an empty string as a 3rd parameter to .css will automatically\n\t\t\t// attempt a parseFloat and fallback to a string if the parse fails.\n\t\t\t// Simple values such as \"10px\" are parsed to Float;\n\t\t\t// complex values such as \"rotate(1rad)\" are returned as-is.\n\t\t\tresult = jQuery.css( tween.elem, tween.prop, \"\" );\n\n\t\t\t// Empty strings, null, undefined and \"auto\" are converted to 0.\n\t\t\treturn !result || result === \"auto\" ? 0 : result;\n\t\t},\n\t\tset: function( tween ) {\n\n\t\t\t// Use step hook for back compat.\n\t\t\t// Use cssHook if its there.\n\t\t\t// Use .style if available and use plain properties where available.\n\t\t\tif ( jQuery.fx.step[ tween.prop ] ) {\n\t\t\t\tjQuery.fx.step[ tween.prop ]( tween );\n\t\t\t} else if ( tween.elem.nodeType === 1 &&\n\t\t\t\t( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null ||\n\t\t\t\t\tjQuery.cssHooks[ tween.prop ] ) ) {\n\t\t\t\tjQuery.style( tween.elem, tween.prop, tween.now + tween.unit );\n\t\t\t} else {\n\t\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t\t}\n\t\t}\n\t}\n};\n\n// Support: IE9\n// Panic based approach to setting things on disconnected nodes\nTween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {\n\tset: function( tween ) {\n\t\tif ( tween.elem.nodeType && tween.elem.parentNode ) {\n\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t}\n\t}\n};\n\njQuery.easing = {\n\tlinear: function( p ) {\n\t\treturn p;\n\t},\n\tswing: function( p ) {\n\t\treturn 0.5 - Math.cos( p * Math.PI ) / 2;\n\t},\n\t_default: \"swing\"\n};\n\njQuery.fx = Tween.prototype.init;\n\n// Back Compat <1.8 extension point\njQuery.fx.step = {};\n\n\n\n\nvar\n\tfxNow, timerId,\n\trfxtypes = /^(?:toggle|show|hide)$/,\n\trrun = /queueHooks$/;\n\n// Animations created synchronously will run synchronously\nfunction createFxNow() {\n\twindow.setTimeout( function() {\n\t\tfxNow = undefined;\n\t} );\n\treturn ( fxNow = jQuery.now() );\n}\n\n// Generate parameters to create a standard animation\nfunction genFx( type, includeWidth ) {\n\tvar which,\n\t\ti = 0,\n\t\tattrs = { height: type };\n\n\t// If we include width, step value is 1 to do all cssExpand values,\n\t// otherwise step value is 2 to skip over Left and Right\n\tincludeWidth = includeWidth ? 1 : 0;\n\tfor ( ; i < 4 ; i += 2 - includeWidth ) {\n\t\twhich = cssExpand[ i ];\n\t\tattrs[ \"margin\" + which ] = attrs[ \"padding\" + which ] = type;\n\t}\n\n\tif ( includeWidth ) {\n\t\tattrs.opacity = attrs.width = type;\n\t}\n\n\treturn attrs;\n}\n\nfunction createTween( value, prop, animation ) {\n\tvar tween,\n\t\tcollection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ \"*\" ] ),\n\t\tindex = 0,\n\t\tlength = collection.length;\n\tfor ( ; index < length; index++ ) {\n\t\tif ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {\n\n\t\t\t// We're done with this property\n\t\t\treturn tween;\n\t\t}\n\t}\n}\n\nfunction defaultPrefilter( elem, props, opts ) {\n\t/* jshint validthis: true */\n\tvar prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,\n\t\tanim = this,\n\t\torig = {},\n\t\tstyle = elem.style,\n\t\thidden = elem.nodeType && isHidden( elem ),\n\t\tdataShow = dataPriv.get( elem, \"fxshow\" );\n\n\t// Handle queue: false promises\n\tif ( !opts.queue ) {\n\t\thooks = jQuery._queueHooks( elem, \"fx\" );\n\t\tif ( hooks.unqueued == null ) {\n\t\t\thooks.unqueued = 0;\n\t\t\toldfire = hooks.empty.fire;\n\t\t\thooks.empty.fire = function() {\n\t\t\t\tif ( !hooks.unqueued ) {\n\t\t\t\t\toldfire();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\thooks.unqueued++;\n\n\t\tanim.always( function() {\n\n\t\t\t// Ensure the complete handler is called before this completes\n\t\t\tanim.always( function() {\n\t\t\t\thooks.unqueued--;\n\t\t\t\tif ( !jQuery.queue( elem, \"fx\" ).length ) {\n\t\t\t\t\thooks.empty.fire();\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\t// Height/width overflow pass\n\tif ( elem.nodeType === 1 && ( \"height\" in props || \"width\" in props ) ) {\n\n\t\t// Make sure that nothing sneaks out\n\t\t// Record all 3 overflow attributes because IE9-10 do not\n\t\t// change the overflow attribute when overflowX and\n\t\t// overflowY are set to the same value\n\t\topts.overflow = [ style.overflow, style.overflowX, style.overflowY ];\n\n\t\t// Set display property to inline-block for height/width\n\t\t// animations on inline elements that are having width/height animated\n\t\tdisplay = jQuery.css( elem, \"display\" );\n\n\t\t// Test default display if display is currently \"none\"\n\t\tcheckDisplay = display === \"none\" ?\n\t\t\tdataPriv.get( elem, \"olddisplay\" ) || defaultDisplay( elem.nodeName ) : display;\n\n\t\tif ( checkDisplay === \"inline\" && jQuery.css( elem, \"float\" ) === \"none\" ) {\n\t\t\tstyle.display = \"inline-block\";\n\t\t}\n\t}\n\n\tif ( opts.overflow ) {\n\t\tstyle.overflow = \"hidden\";\n\t\tanim.always( function() {\n\t\t\tstyle.overflow = opts.overflow[ 0 ];\n\t\t\tstyle.overflowX = opts.overflow[ 1 ];\n\t\t\tstyle.overflowY = opts.overflow[ 2 ];\n\t\t} );\n\t}\n\n\t// show/hide pass\n\tfor ( prop in props ) {\n\t\tvalue = props[ prop ];\n\t\tif ( rfxtypes.exec( value ) ) {\n\t\t\tdelete props[ prop ];\n\t\t\ttoggle = toggle || value === \"toggle\";\n\t\t\tif ( value === ( hidden ? \"hide\" : \"show\" ) ) {\n\n\t\t\t\t// If there is dataShow left over from a stopped hide or show\n\t\t\t\t// and we are going to proceed with show, we should pretend to be hidden\n\t\t\t\tif ( value === \"show\" && dataShow && dataShow[ prop ] !== undefined ) {\n\t\t\t\t\thidden = true;\n\t\t\t\t} else {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\torig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );\n\n\t\t// Any non-fx value stops us from restoring the original display value\n\t\t} else {\n\t\t\tdisplay = undefined;\n\t\t}\n\t}\n\n\tif ( !jQuery.isEmptyObject( orig ) ) {\n\t\tif ( dataShow ) {\n\t\t\tif ( \"hidden\" in dataShow ) {\n\t\t\t\thidden = dataShow.hidden;\n\t\t\t}\n\t\t} else {\n\t\t\tdataShow = dataPriv.access( elem, \"fxshow\", {} );\n\t\t}\n\n\t\t// Store state if its toggle - enables .stop().toggle() to \"reverse\"\n\t\tif ( toggle ) {\n\t\t\tdataShow.hidden = !hidden;\n\t\t}\n\t\tif ( hidden ) {\n\t\t\tjQuery( elem ).show();\n\t\t} else {\n\t\t\tanim.done( function() {\n\t\t\t\tjQuery( elem ).hide();\n\t\t\t} );\n\t\t}\n\t\tanim.done( function() {\n\t\t\tvar prop;\n\n\t\t\tdataPriv.remove( elem, \"fxshow\" );\n\t\t\tfor ( prop in orig ) {\n\t\t\t\tjQuery.style( elem, prop, orig[ prop ] );\n\t\t\t}\n\t\t} );\n\t\tfor ( prop in orig ) {\n\t\t\ttween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );\n\n\t\t\tif ( !( prop in dataShow ) ) {\n\t\t\t\tdataShow[ prop ] = tween.start;\n\t\t\t\tif ( hidden ) {\n\t\t\t\t\ttween.end = tween.start;\n\t\t\t\t\ttween.start = prop === \"width\" || prop === \"height\" ? 1 : 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t// If this is a noop like .hide().hide(), restore an overwritten display value\n\t} else if ( ( display === \"none\" ? defaultDisplay( elem.nodeName ) : display ) === \"inline\" ) {\n\t\tstyle.display = display;\n\t}\n}\n\nfunction propFilter( props, specialEasing ) {\n\tvar index, name, easing, value, hooks;\n\n\t// camelCase, specialEasing and expand cssHook pass\n\tfor ( index in props ) {\n\t\tname = jQuery.camelCase( index );\n\t\teasing = specialEasing[ name ];\n\t\tvalue = props[ index ];\n\t\tif ( jQuery.isArray( value ) ) {\n\t\t\teasing = value[ 1 ];\n\t\t\tvalue = props[ index ] = value[ 0 ];\n\t\t}\n\n\t\tif ( index !== name ) {\n\t\t\tprops[ name ] = value;\n\t\t\tdelete props[ index ];\n\t\t}\n\n\t\thooks = jQuery.cssHooks[ name ];\n\t\tif ( hooks && \"expand\" in hooks ) {\n\t\t\tvalue = hooks.expand( value );\n\t\t\tdelete props[ name ];\n\n\t\t\t// Not quite $.extend, this won't overwrite existing keys.\n\t\t\t// Reusing 'index' because we have the correct \"name\"\n\t\t\tfor ( index in value ) {\n\t\t\t\tif ( !( index in props ) ) {\n\t\t\t\t\tprops[ index ] = value[ index ];\n\t\t\t\t\tspecialEasing[ index ] = easing;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tspecialEasing[ name ] = easing;\n\t\t}\n\t}\n}\n\nfunction Animation( elem, properties, options ) {\n\tvar result,\n\t\tstopped,\n\t\tindex = 0,\n\t\tlength = Animation.prefilters.length,\n\t\tdeferred = jQuery.Deferred().always( function() {\n\n\t\t\t// Don't match elem in the :animated selector\n\t\t\tdelete tick.elem;\n\t\t} ),\n\t\ttick = function() {\n\t\t\tif ( stopped ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvar currentTime = fxNow || createFxNow(),\n\t\t\t\tremaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),\n\n\t\t\t\t// Support: Android 2.3\n\t\t\t\t// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)\n\t\t\t\ttemp = remaining / animation.duration || 0,\n\t\t\t\tpercent = 1 - temp,\n\t\t\t\tindex = 0,\n\t\t\t\tlength = animation.tweens.length;\n\n\t\t\tfor ( ; index < length ; index++ ) {\n\t\t\t\tanimation.tweens[ index ].run( percent );\n\t\t\t}\n\n\t\t\tdeferred.notifyWith( elem, [ animation, percent, remaining ] );\n\n\t\t\tif ( percent < 1 && length ) {\n\t\t\t\treturn remaining;\n\t\t\t} else {\n\t\t\t\tdeferred.resolveWith( elem, [ animation ] );\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\t\tanimation = deferred.promise( {\n\t\t\telem: elem,\n\t\t\tprops: jQuery.extend( {}, properties ),\n\t\t\topts: jQuery.extend( true, {\n\t\t\t\tspecialEasing: {},\n\t\t\t\teasing: jQuery.easing._default\n\t\t\t}, options ),\n\t\t\toriginalProperties: properties,\n\t\t\toriginalOptions: options,\n\t\t\tstartTime: fxNow || createFxNow(),\n\t\t\tduration: options.duration,\n\t\t\ttweens: [],\n\t\t\tcreateTween: function( prop, end ) {\n\t\t\t\tvar tween = jQuery.Tween( elem, animation.opts, prop, end,\n\t\t\t\t\t\tanimation.opts.specialEasing[ prop ] || animation.opts.easing );\n\t\t\t\tanimation.tweens.push( tween );\n\t\t\t\treturn tween;\n\t\t\t},\n\t\t\tstop: function( gotoEnd ) {\n\t\t\t\tvar index = 0,\n\n\t\t\t\t\t// If we are going to the end, we want to run all the tweens\n\t\t\t\t\t// otherwise we skip this part\n\t\t\t\t\tlength = gotoEnd ? animation.tweens.length : 0;\n\t\t\t\tif ( stopped ) {\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t\tstopped = true;\n\t\t\t\tfor ( ; index < length ; index++ ) {\n\t\t\t\t\tanimation.tweens[ index ].run( 1 );\n\t\t\t\t}\n\n\t\t\t\t// Resolve when we played the last frame; otherwise, reject\n\t\t\t\tif ( gotoEnd ) {\n\t\t\t\t\tdeferred.notifyWith( elem, [ animation, 1, 0 ] );\n\t\t\t\t\tdeferred.resolveWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t} else {\n\t\t\t\t\tdeferred.rejectWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\t\t} ),\n\t\tprops = animation.props;\n\n\tpropFilter( props, animation.opts.specialEasing );\n\n\tfor ( ; index < length ; index++ ) {\n\t\tresult = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );\n\t\tif ( result ) {\n\t\t\tif ( jQuery.isFunction( result.stop ) ) {\n\t\t\t\tjQuery._queueHooks( animation.elem, animation.opts.queue ).stop =\n\t\t\t\t\tjQuery.proxy( result.stop, result );\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tjQuery.map( props, createTween, animation );\n\n\tif ( jQuery.isFunction( animation.opts.start ) ) {\n\t\tanimation.opts.start.call( elem, animation );\n\t}\n\n\tjQuery.fx.timer(\n\t\tjQuery.extend( tick, {\n\t\t\telem: elem,\n\t\t\tanim: animation,\n\t\t\tqueue: animation.opts.queue\n\t\t} )\n\t);\n\n\t// attach callbacks from options\n\treturn animation.progress( animation.opts.progress )\n\t\t.done( animation.opts.done, animation.opts.complete )\n\t\t.fail( animation.opts.fail )\n\t\t.always( animation.opts.always );\n}\n\njQuery.Animation = jQuery.extend( Animation, {\n\ttweeners: {\n\t\t\"*\": [ function( prop, value ) {\n\t\t\tvar tween = this.createTween( prop, value );\n\t\t\tadjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );\n\t\t\treturn tween;\n\t\t} ]\n\t},\n\n\ttweener: function( props, callback ) {\n\t\tif ( jQuery.isFunction( props ) ) {\n\t\t\tcallback = props;\n\t\t\tprops = [ \"*\" ];\n\t\t} else {\n\t\t\tprops = props.match( rnotwhite );\n\t\t}\n\n\t\tvar prop,\n\t\t\tindex = 0,\n\t\t\tlength = props.length;\n\n\t\tfor ( ; index < length ; index++ ) {\n\t\t\tprop = props[ index ];\n\t\t\tAnimation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];\n\t\t\tAnimation.tweeners[ prop ].unshift( callback );\n\t\t}\n\t},\n\n\tprefilters: [ defaultPrefilter ],\n\n\tprefilter: function( callback, prepend ) {\n\t\tif ( prepend ) {\n\t\t\tAnimation.prefilters.unshift( callback );\n\t\t} else {\n\t\t\tAnimation.prefilters.push( callback );\n\t\t}\n\t}\n} );\n\njQuery.speed = function( speed, easing, fn ) {\n\tvar opt = speed && typeof speed === \"object\" ? jQuery.extend( {}, speed ) : {\n\t\tcomplete: fn || !fn && easing ||\n\t\t\tjQuery.isFunction( speed ) && speed,\n\t\tduration: speed,\n\t\teasing: fn && easing || easing && !jQuery.isFunction( easing ) && easing\n\t};\n\n\topt.duration = jQuery.fx.off ? 0 : typeof opt.duration === \"number\" ?\n\t\topt.duration : opt.duration in jQuery.fx.speeds ?\n\t\t\tjQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;\n\n\t// Normalize opt.queue - true/undefined/null -> \"fx\"\n\tif ( opt.queue == null || opt.queue === true ) {\n\t\topt.queue = \"fx\";\n\t}\n\n\t// Queueing\n\topt.old = opt.complete;\n\n\topt.complete = function() {\n\t\tif ( jQuery.isFunction( opt.old ) ) {\n\t\t\topt.old.call( this );\n\t\t}\n\n\t\tif ( opt.queue ) {\n\t\t\tjQuery.dequeue( this, opt.queue );\n\t\t}\n\t};\n\n\treturn opt;\n};\n\njQuery.fn.extend( {\n\tfadeTo: function( speed, to, easing, callback ) {\n\n\t\t// Show any hidden elements after setting opacity to 0\n\t\treturn this.filter( isHidden ).css( \"opacity\", 0 ).show()\n\n\t\t\t// Animate to the value specified\n\t\t\t.end().animate( { opacity: to }, speed, easing, callback );\n\t},\n\tanimate: function( prop, speed, easing, callback ) {\n\t\tvar empty = jQuery.isEmptyObject( prop ),\n\t\t\toptall = jQuery.speed( speed, easing, callback ),\n\t\t\tdoAnimation = function() {\n\n\t\t\t\t// Operate on a copy of prop so per-property easing won't be lost\n\t\t\t\tvar anim = Animation( this, jQuery.extend( {}, prop ), optall );\n\n\t\t\t\t// Empty animations, or finishing resolves immediately\n\t\t\t\tif ( empty || dataPriv.get( this, \"finish\" ) ) {\n\t\t\t\t\tanim.stop( true );\n\t\t\t\t}\n\t\t\t};\n\t\t\tdoAnimation.finish = doAnimation;\n\n\t\treturn empty || optall.queue === false ?\n\t\t\tthis.each( doAnimation ) :\n\t\t\tthis.queue( optall.queue, doAnimation );\n\t},\n\tstop: function( type, clearQueue, gotoEnd ) {\n\t\tvar stopQueue = function( hooks ) {\n\t\t\tvar stop = hooks.stop;\n\t\t\tdelete hooks.stop;\n\t\t\tstop( gotoEnd );\n\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tgotoEnd = clearQueue;\n\t\t\tclearQueue = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\tif ( clearQueue && type !== false ) {\n\t\t\tthis.queue( type || \"fx\", [] );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar dequeue = true,\n\t\t\t\tindex = type != null && type + \"queueHooks\",\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tdata = dataPriv.get( this );\n\n\t\t\tif ( index ) {\n\t\t\t\tif ( data[ index ] && data[ index ].stop ) {\n\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( index in data ) {\n\t\t\t\t\tif ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {\n\t\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this &&\n\t\t\t\t\t( type == null || timers[ index ].queue === type ) ) {\n\n\t\t\t\t\ttimers[ index ].anim.stop( gotoEnd );\n\t\t\t\t\tdequeue = false;\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Start the next in the queue if the last step wasn't forced.\n\t\t\t// Timers currently will call their complete callbacks, which\n\t\t\t// will dequeue but only if they were gotoEnd.\n\t\t\tif ( dequeue || !gotoEnd ) {\n\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t}\n\t\t} );\n\t},\n\tfinish: function( type ) {\n\t\tif ( type !== false ) {\n\t\t\ttype = type || \"fx\";\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tvar index,\n\t\t\t\tdata = dataPriv.get( this ),\n\t\t\t\tqueue = data[ type + \"queue\" ],\n\t\t\t\thooks = data[ type + \"queueHooks\" ],\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tlength = queue ? queue.length : 0;\n\n\t\t\t// Enable finishing flag on private data\n\t\t\tdata.finish = true;\n\n\t\t\t// Empty the queue first\n\t\t\tjQuery.queue( this, type, [] );\n\n\t\t\tif ( hooks && hooks.stop ) {\n\t\t\t\thooks.stop.call( this, true );\n\t\t\t}\n\n\t\t\t// Look for any active animations, and finish them\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this && timers[ index ].queue === type ) {\n\t\t\t\t\ttimers[ index ].anim.stop( true );\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Look for any animations in the old queue and finish them\n\t\t\tfor ( index = 0; index < length; index++ ) {\n\t\t\t\tif ( queue[ index ] && queue[ index ].finish ) {\n\t\t\t\t\tqueue[ index ].finish.call( this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Turn off finishing flag\n\t\t\tdelete data.finish;\n\t\t} );\n\t}\n} );\n\njQuery.each( [ \"toggle\", \"show\", \"hide\" ], function( i, name ) {\n\tvar cssFn = jQuery.fn[ name ];\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn speed == null || typeof speed === \"boolean\" ?\n\t\t\tcssFn.apply( this, arguments ) :\n\t\t\tthis.animate( genFx( name, true ), speed, easing, callback );\n\t};\n} );\n\n// Generate shortcuts for custom animations\njQuery.each( {\n\tslideDown: genFx( \"show\" ),\n\tslideUp: genFx( \"hide\" ),\n\tslideToggle: genFx( \"toggle\" ),\n\tfadeIn: { opacity: \"show\" },\n\tfadeOut: { opacity: \"hide\" },\n\tfadeToggle: { opacity: \"toggle\" }\n}, function( name, props ) {\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn this.animate( props, speed, easing, callback );\n\t};\n} );\n\njQuery.timers = [];\njQuery.fx.tick = function() {\n\tvar timer,\n\t\ti = 0,\n\t\ttimers = jQuery.timers;\n\n\tfxNow = jQuery.now();\n\n\tfor ( ; i < timers.length; i++ ) {\n\t\ttimer = timers[ i ];\n\n\t\t// Checks the timer has not already been removed\n\t\tif ( !timer() && timers[ i ] === timer ) {\n\t\t\ttimers.splice( i--, 1 );\n\t\t}\n\t}\n\n\tif ( !timers.length ) {\n\t\tjQuery.fx.stop();\n\t}\n\tfxNow = undefined;\n};\n\njQuery.fx.timer = function( timer ) {\n\tjQuery.timers.push( timer );\n\tif ( timer() ) {\n\t\tjQuery.fx.start();\n\t} else {\n\t\tjQuery.timers.pop();\n\t}\n};\n\njQuery.fx.interval = 13;\njQuery.fx.start = function() {\n\tif ( !timerId ) {\n\t\ttimerId = window.setInterval( jQuery.fx.tick, jQuery.fx.interval );\n\t}\n};\n\njQuery.fx.stop = function() {\n\twindow.clearInterval( timerId );\n\n\ttimerId = null;\n};\n\njQuery.fx.speeds = {\n\tslow: 600,\n\tfast: 200,\n\n\t// Default speed\n\t_default: 400\n};\n\n\n// Based off of the plugin by Clint Helfers, with permission.\n// http://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/\njQuery.fn.delay = function( time, type ) {\n\ttime = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;\n\ttype = type || \"fx\";\n\n\treturn this.queue( type, function( next, hooks ) {\n\t\tvar timeout = window.setTimeout( next, time );\n\t\thooks.stop = function() {\n\t\t\twindow.clearTimeout( timeout );\n\t\t};\n\t} );\n};\n\n\n( function() {\n\tvar input = document.createElement( \"input\" ),\n\t\tselect = document.createElement( \"select\" ),\n\t\topt = select.appendChild( document.createElement( \"option\" ) );\n\n\tinput.type = \"checkbox\";\n\n\t// Support: iOS<=5.1, Android<=4.2+\n\t// Default value for a checkbox should be \"on\"\n\tsupport.checkOn = input.value !== \"\";\n\n\t// Support: IE<=11+\n\t// Must access selectedIndex to make default options select\n\tsupport.optSelected = opt.selected;\n\n\t// Support: Android<=2.3\n\t// Options inside disabled selects are incorrectly marked as disabled\n\tselect.disabled = true;\n\tsupport.optDisabled = !opt.disabled;\n\n\t// Support: IE<=11+\n\t// An input loses its value after becoming a radio\n\tinput = document.createElement( \"input\" );\n\tinput.value = \"t\";\n\tinput.type = \"radio\";\n\tsupport.radioValue = input.value === \"t\";\n} )();\n\n\nvar boolHook,\n\tattrHandle = jQuery.expr.attrHandle;\n\njQuery.fn.extend( {\n\tattr: function( name, value ) {\n\t\treturn access( this, jQuery.attr, name, value, arguments.length > 1 );\n\t},\n\n\tremoveAttr: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.removeAttr( this, name );\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tattr: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set attributes on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Fallback to prop when attributes are not supported\n\t\tif ( typeof elem.getAttribute === \"undefined\" ) {\n\t\t\treturn jQuery.prop( elem, name, value );\n\t\t}\n\n\t\t// All attributes are lowercase\n\t\t// Grab necessary hook if one is defined\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\t\t\tname = name.toLowerCase();\n\t\t\thooks = jQuery.attrHooks[ name ] ||\n\t\t\t\t( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( value === null ) {\n\t\t\t\tjQuery.removeAttr( elem, name );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\telem.setAttribute( name, value + \"\" );\n\t\t\treturn value;\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\tret = jQuery.find.attr( elem, name );\n\n\t\t// Non-existent attributes return null, we normalize to undefined\n\t\treturn ret == null ? undefined : ret;\n\t},\n\n\tattrHooks: {\n\t\ttype: {\n\t\t\tset: function( elem, value ) {\n\t\t\t\tif ( !support.radioValue && value === \"radio\" &&\n\t\t\t\t\tjQuery.nodeName( elem, \"input\" ) ) {\n\t\t\t\t\tvar val = elem.value;\n\t\t\t\t\telem.setAttribute( \"type\", value );\n\t\t\t\t\tif ( val ) {\n\t\t\t\t\t\telem.value = val;\n\t\t\t\t\t}\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tremoveAttr: function( elem, value ) {\n\t\tvar name, propName,\n\t\t\ti = 0,\n\t\t\tattrNames = value && value.match( rnotwhite );\n\n\t\tif ( attrNames && elem.nodeType === 1 ) {\n\t\t\twhile ( ( name = attrNames[ i++ ] ) ) {\n\t\t\t\tpropName = jQuery.propFix[ name ] || name;\n\n\t\t\t\t// Boolean attributes get special treatment (#10870)\n\t\t\t\tif ( jQuery.expr.match.bool.test( name ) ) {\n\n\t\t\t\t\t// Set corresponding property to false\n\t\t\t\t\telem[ propName ] = false;\n\t\t\t\t}\n\n\t\t\t\telem.removeAttribute( name );\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Hooks for boolean attributes\nboolHook = {\n\tset: function( elem, value, name ) {\n\t\tif ( value === false ) {\n\n\t\t\t// Remove boolean attributes when set to false\n\t\t\tjQuery.removeAttr( elem, name );\n\t\t} else {\n\t\t\telem.setAttribute( name, name );\n\t\t}\n\t\treturn name;\n\t}\n};\njQuery.each( jQuery.expr.match.bool.source.match( /\\w+/g ), function( i, name ) {\n\tvar getter = attrHandle[ name ] || jQuery.find.attr;\n\n\tattrHandle[ name ] = function( elem, name, isXML ) {\n\t\tvar ret, handle;\n\t\tif ( !isXML ) {\n\n\t\t\t// Avoid an infinite loop by temporarily removing this function from the getter\n\t\t\thandle = attrHandle[ name ];\n\t\t\tattrHandle[ name ] = ret;\n\t\t\tret = getter( elem, name, isXML ) != null ?\n\t\t\t\tname.toLowerCase() :\n\t\t\t\tnull;\n\t\t\tattrHandle[ name ] = handle;\n\t\t}\n\t\treturn ret;\n\t};\n} );\n\n\n\n\nvar rfocusable = /^(?:input|select|textarea|button)$/i,\n\trclickable = /^(?:a|area)$/i;\n\njQuery.fn.extend( {\n\tprop: function( name, value ) {\n\t\treturn access( this, jQuery.prop, name, value, arguments.length > 1 );\n\t},\n\n\tremoveProp: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tdelete this[ jQuery.propFix[ name ] || name ];\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tprop: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set properties on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// Fix name and attach hooks\n\t\t\tname = jQuery.propFix[ name ] || name;\n\t\t\thooks = jQuery.propHooks[ name ];\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\treturn ( elem[ name ] = value );\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\treturn elem[ name ];\n\t},\n\n\tpropHooks: {\n\t\ttabIndex: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\t// elem.tabIndex doesn't always return the\n\t\t\t\t// correct value when it hasn't been explicitly set\n\t\t\t\t// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/\n\t\t\t\t// Use proper attribute retrieval(#12072)\n\t\t\t\tvar tabindex = jQuery.find.attr( elem, \"tabindex\" );\n\n\t\t\t\treturn tabindex ?\n\t\t\t\t\tparseInt( tabindex, 10 ) :\n\t\t\t\t\trfocusable.test( elem.nodeName ) ||\n\t\t\t\t\t\trclickable.test( elem.nodeName ) && elem.href ?\n\t\t\t\t\t\t\t0 :\n\t\t\t\t\t\t\t-1;\n\t\t\t}\n\t\t}\n\t},\n\n\tpropFix: {\n\t\t\"for\": \"htmlFor\",\n\t\t\"class\": \"className\"\n\t}\n} );\n\nif ( !support.optSelected ) {\n\tjQuery.propHooks.selected = {\n\t\tget: function( elem ) {\n\t\t\tvar parent = elem.parentNode;\n\t\t\tif ( parent && parent.parentNode ) {\n\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t};\n}\n\njQuery.each( [\n\t\"tabIndex\",\n\t\"readOnly\",\n\t\"maxLength\",\n\t\"cellSpacing\",\n\t\"cellPadding\",\n\t\"rowSpan\",\n\t\"colSpan\",\n\t\"useMap\",\n\t\"frameBorder\",\n\t\"contentEditable\"\n], function() {\n\tjQuery.propFix[ this.toLowerCase() ] = this;\n} );\n\n\n\n\nvar rclass = /[\\t\\r\\n\\f]/g;\n\nfunction getClass( elem ) {\n\treturn elem.getAttribute && elem.getAttribute( \"class\" ) || \"\";\n}\n\njQuery.fn.extend( {\n\taddClass: function( value ) {\n\t\tvar classes, elem, cur, curValue, clazz, j, finalValue,\n\t\t\ti = 0;\n\n\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).addClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tif ( typeof value === \"string\" && value ) {\n\t\t\tclasses = value.match( rnotwhite ) || [];\n\n\t\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\t\tcurValue = getClass( elem );\n\t\t\t\tcur = elem.nodeType === 1 &&\n\t\t\t\t\t( \" \" + curValue + \" \" ).replace( rclass, \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\twhile ( ( clazz = classes[ j++ ] ) ) {\n\t\t\t\t\t\tif ( cur.indexOf( \" \" + clazz + \" \" ) < 0 ) {\n\t\t\t\t\t\t\tcur += clazz + \" \";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = jQuery.trim( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\telem.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tremoveClass: function( value ) {\n\t\tvar classes, elem, cur, curValue, clazz, j, finalValue,\n\t\t\ti = 0;\n\n\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tif ( !arguments.length ) {\n\t\t\treturn this.attr( \"class\", \"\" );\n\t\t}\n\n\t\tif ( typeof value === \"string\" && value ) {\n\t\t\tclasses = value.match( rnotwhite ) || [];\n\n\t\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\t\tcurValue = getClass( elem );\n\n\t\t\t\t// This expression is here for better compressibility (see addClass)\n\t\t\t\tcur = elem.nodeType === 1 &&\n\t\t\t\t\t( \" \" + curValue + \" \" ).replace( rclass, \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\twhile ( ( clazz = classes[ j++ ] ) ) {\n\n\t\t\t\t\t\t// Remove *all* instances\n\t\t\t\t\t\twhile ( cur.indexOf( \" \" + clazz + \" \" ) > -1 ) {\n\t\t\t\t\t\t\tcur = cur.replace( \" \" + clazz + \" \", \" \" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = jQuery.trim( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\telem.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\ttoggleClass: function( value, stateVal ) {\n\t\tvar type = typeof value;\n\n\t\tif ( typeof stateVal === \"boolean\" && type === \"string\" ) {\n\t\t\treturn stateVal ? this.addClass( value ) : this.removeClass( value );\n\t\t}\n\n\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).toggleClass(\n\t\t\t\t\tvalue.call( this, i, getClass( this ), stateVal ),\n\t\t\t\t\tstateVal\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar className, i, self, classNames;\n\n\t\t\tif ( type === \"string\" ) {\n\n\t\t\t\t// Toggle individual class names\n\t\t\t\ti = 0;\n\t\t\t\tself = jQuery( this );\n\t\t\t\tclassNames = value.match( rnotwhite ) || [];\n\n\t\t\t\twhile ( ( className = classNames[ i++ ] ) ) {\n\n\t\t\t\t\t// Check each className given, space separated list\n\t\t\t\t\tif ( self.hasClass( className ) ) {\n\t\t\t\t\t\tself.removeClass( className );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tself.addClass( className );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t// Toggle whole class name\n\t\t\t} else if ( value === undefined || type === \"boolean\" ) {\n\t\t\t\tclassName = getClass( this );\n\t\t\t\tif ( className ) {\n\n\t\t\t\t\t// Store className if set\n\t\t\t\t\tdataPriv.set( this, \"__className__\", className );\n\t\t\t\t}\n\n\t\t\t\t// If the element has a class name or if we're passed `false`,\n\t\t\t\t// then remove the whole classname (if there was one, the above saved it).\n\t\t\t\t// Otherwise bring back whatever was previously saved (if anything),\n\t\t\t\t// falling back to the empty string if nothing was stored.\n\t\t\t\tif ( this.setAttribute ) {\n\t\t\t\t\tthis.setAttribute( \"class\",\n\t\t\t\t\t\tclassName || value === false ?\n\t\t\t\t\t\t\"\" :\n\t\t\t\t\t\tdataPriv.get( this, \"__className__\" ) || \"\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\thasClass: function( selector ) {\n\t\tvar className, elem,\n\t\t\ti = 0;\n\n\t\tclassName = \" \" + selector + \" \";\n\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\tif ( elem.nodeType === 1 &&\n\t\t\t\t( \" \" + getClass( elem ) + \" \" ).replace( rclass, \" \" )\n\t\t\t\t\t.indexOf( className ) > -1\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n} );\n\n\n\n\nvar rreturn = /\\r/g;\n\njQuery.fn.extend( {\n\tval: function( value ) {\n\t\tvar hooks, ret, isFunction,\n\t\t\telem = this[ 0 ];\n\n\t\tif ( !arguments.length ) {\n\t\t\tif ( elem ) {\n\t\t\t\thooks = jQuery.valHooks[ elem.type ] ||\n\t\t\t\t\tjQuery.valHooks[ elem.nodeName.toLowerCase() ];\n\n\t\t\t\tif ( hooks &&\n\t\t\t\t\t\"get\" in hooks &&\n\t\t\t\t\t( ret = hooks.get( elem, \"value\" ) ) !== undefined\n\t\t\t\t) {\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tret = elem.value;\n\n\t\t\t\treturn typeof ret === \"string\" ?\n\n\t\t\t\t\t// Handle most common string cases\n\t\t\t\t\tret.replace( rreturn, \"\" ) :\n\n\t\t\t\t\t// Handle cases where value is null/undef or number\n\t\t\t\t\tret == null ? \"\" : ret;\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tisFunction = jQuery.isFunction( value );\n\n\t\treturn this.each( function( i ) {\n\t\t\tvar val;\n\n\t\t\tif ( this.nodeType !== 1 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( isFunction ) {\n\t\t\t\tval = value.call( this, i, jQuery( this ).val() );\n\t\t\t} else {\n\t\t\t\tval = value;\n\t\t\t}\n\n\t\t\t// Treat null/undefined as \"\"; convert numbers to string\n\t\t\tif ( val == null ) {\n\t\t\t\tval = \"\";\n\n\t\t\t} else if ( typeof val === \"number\" ) {\n\t\t\t\tval += \"\";\n\n\t\t\t} else if ( jQuery.isArray( val ) ) {\n\t\t\t\tval = jQuery.map( val, function( value ) {\n\t\t\t\t\treturn value == null ? \"\" : value + \"\";\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\thooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];\n\n\t\t\t// If set returns undefined, fall back to normal setting\n\t\t\tif ( !hooks || !( \"set\" in hooks ) || hooks.set( this, val, \"value\" ) === undefined ) {\n\t\t\t\tthis.value = val;\n\t\t\t}\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tvalHooks: {\n\t\toption: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\t// Support: IE<11\n\t\t\t\t// option.value not trimmed (#14858)\n\t\t\t\treturn jQuery.trim( elem.value );\n\t\t\t}\n\t\t},\n\t\tselect: {\n\t\t\tget: function( elem ) {\n\t\t\t\tvar value, option,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tindex = elem.selectedIndex,\n\t\t\t\t\tone = elem.type === \"select-one\" || index < 0,\n\t\t\t\t\tvalues = one ? null : [],\n\t\t\t\t\tmax = one ? index + 1 : options.length,\n\t\t\t\t\ti = index < 0 ?\n\t\t\t\t\t\tmax :\n\t\t\t\t\t\tone ? index : 0;\n\n\t\t\t\t// Loop through all the selected options\n\t\t\t\tfor ( ; i < max; i++ ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t// IE8-9 doesn't update selected after form reset (#2551)\n\t\t\t\t\tif ( ( option.selected || i === index ) &&\n\n\t\t\t\t\t\t\t// Don't return options that are disabled or in a disabled optgroup\n\t\t\t\t\t\t\t( support.optDisabled ?\n\t\t\t\t\t\t\t\t!option.disabled : option.getAttribute( \"disabled\" ) === null ) &&\n\t\t\t\t\t\t\t( !option.parentNode.disabled ||\n\t\t\t\t\t\t\t\t!jQuery.nodeName( option.parentNode, \"optgroup\" ) ) ) {\n\n\t\t\t\t\t\t// Get the specific value for the option\n\t\t\t\t\t\tvalue = jQuery( option ).val();\n\n\t\t\t\t\t\t// We don't need an array for one selects\n\t\t\t\t\t\tif ( one ) {\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Multi-Selects return an array\n\t\t\t\t\t\tvalues.push( value );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn values;\n\t\t\t},\n\n\t\t\tset: function( elem, value ) {\n\t\t\t\tvar optionSet, option,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tvalues = jQuery.makeArray( value ),\n\t\t\t\t\ti = options.length;\n\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\toption = options[ i ];\n\t\t\t\t\tif ( option.selected =\n\t\t\t\t\t\t\tjQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1\n\t\t\t\t\t) {\n\t\t\t\t\t\toptionSet = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Force browsers to behave consistently when non-matching value is set\n\t\t\t\tif ( !optionSet ) {\n\t\t\t\t\telem.selectedIndex = -1;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Radios and checkboxes getter/setter\njQuery.each( [ \"radio\", \"checkbox\" ], function() {\n\tjQuery.valHooks[ this ] = {\n\t\tset: function( elem, value ) {\n\t\t\tif ( jQuery.isArray( value ) ) {\n\t\t\t\treturn ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );\n\t\t\t}\n\t\t}\n\t};\n\tif ( !support.checkOn ) {\n\t\tjQuery.valHooks[ this ].get = function( elem ) {\n\t\t\treturn elem.getAttribute( \"value\" ) === null ? \"on\" : elem.value;\n\t\t};\n\t}\n} );\n\n\n\n\n// Return jQuery for attributes-only inclusion\n\n\nvar rfocusMorph = /^(?:focusinfocus|focusoutblur)$/;\n\njQuery.extend( jQuery.event, {\n\n\ttrigger: function( event, data, elem, onlyHandlers ) {\n\n\t\tvar i, cur, tmp, bubbleType, ontype, handle, special,\n\t\t\teventPath = [ elem || document ],\n\t\t\ttype = hasOwn.call( event, \"type\" ) ? event.type : event,\n\t\t\tnamespaces = hasOwn.call( event, \"namespace\" ) ? event.namespace.split( \".\" ) : [];\n\n\t\tcur = tmp = elem = elem || document;\n\n\t\t// Don't do events on text and comment nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// focus/blur morphs to focusin/out; ensure we're not firing them right now\n\t\tif ( rfocusMorph.test( type + jQuery.event.triggered ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( type.indexOf( \".\" ) > -1 ) {\n\n\t\t\t// Namespaced trigger; create a regexp to match event type in handle()\n\t\t\tnamespaces = type.split( \".\" );\n\t\t\ttype = namespaces.shift();\n\t\t\tnamespaces.sort();\n\t\t}\n\t\tontype = type.indexOf( \":\" ) < 0 && \"on\" + type;\n\n\t\t// Caller can pass in a jQuery.Event object, Object, or just an event type string\n\t\tevent = event[ jQuery.expando ] ?\n\t\t\tevent :\n\t\t\tnew jQuery.Event( type, typeof event === \"object\" && event );\n\n\t\t// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)\n\t\tevent.isTrigger = onlyHandlers ? 2 : 3;\n\t\tevent.namespace = namespaces.join( \".\" );\n\t\tevent.rnamespace = event.namespace ?\n\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" ) :\n\t\t\tnull;\n\n\t\t// Clean up the event in case it is being reused\n\t\tevent.result = undefined;\n\t\tif ( !event.target ) {\n\t\t\tevent.target = elem;\n\t\t}\n\n\t\t// Clone any incoming data and prepend the event, creating the handler arg list\n\t\tdata = data == null ?\n\t\t\t[ event ] :\n\t\t\tjQuery.makeArray( data, [ event ] );\n\n\t\t// Allow special events to draw outside the lines\n\t\tspecial = jQuery.event.special[ type ] || {};\n\t\tif ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine event propagation path in advance, per W3C events spec (#9951)\n\t\t// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)\n\t\tif ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {\n\n\t\t\tbubbleType = special.delegateType || type;\n\t\t\tif ( !rfocusMorph.test( bubbleType + type ) ) {\n\t\t\t\tcur = cur.parentNode;\n\t\t\t}\n\t\t\tfor ( ; cur; cur = cur.parentNode ) {\n\t\t\t\teventPath.push( cur );\n\t\t\t\ttmp = cur;\n\t\t\t}\n\n\t\t\t// Only add window if we got to document (e.g., not plain obj or detached DOM)\n\t\t\tif ( tmp === ( elem.ownerDocument || document ) ) {\n\t\t\t\teventPath.push( tmp.defaultView || tmp.parentWindow || window );\n\t\t\t}\n\t\t}\n\n\t\t// Fire handlers on the event path\n\t\ti = 0;\n\t\twhile ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {\n\n\t\t\tevent.type = i > 1 ?\n\t\t\t\tbubbleType :\n\t\t\t\tspecial.bindType || type;\n\n\t\t\t// jQuery handler\n\t\t\thandle = ( dataPriv.get( cur, \"events\" ) || {} )[ event.type ] &&\n\t\t\t\tdataPriv.get( cur, \"handle\" );\n\t\t\tif ( handle ) {\n\t\t\t\thandle.apply( cur, data );\n\t\t\t}\n\n\t\t\t// Native handler\n\t\t\thandle = ontype && cur[ ontype ];\n\t\t\tif ( handle && handle.apply && acceptData( cur ) ) {\n\t\t\t\tevent.result = handle.apply( cur, data );\n\t\t\t\tif ( event.result === false ) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tevent.type = type;\n\n\t\t// If nobody prevented the default action, do it now\n\t\tif ( !onlyHandlers && !event.isDefaultPrevented() ) {\n\n\t\t\tif ( ( !special._default ||\n\t\t\t\tspecial._default.apply( eventPath.pop(), data ) === false ) &&\n\t\t\t\tacceptData( elem ) ) {\n\n\t\t\t\t// Call a native DOM method on the target with the same name name as the event.\n\t\t\t\t// Don't do default actions on window, that's where global variables be (#6170)\n\t\t\t\tif ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {\n\n\t\t\t\t\t// Don't re-trigger an onFOO event when we call its FOO() method\n\t\t\t\t\ttmp = elem[ ontype ];\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prevent re-triggering of the same event, since we already bubbled it above\n\t\t\t\t\tjQuery.event.triggered = type;\n\t\t\t\t\telem[ type ]();\n\t\t\t\t\tjQuery.event.triggered = undefined;\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = tmp;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\t// Piggyback on a donor event to simulate a different one\n\tsimulate: function( type, elem, event ) {\n\t\tvar e = jQuery.extend(\n\t\t\tnew jQuery.Event(),\n\t\t\tevent,\n\t\t\t{\n\t\t\t\ttype: type,\n\t\t\t\tisSimulated: true\n\n\t\t\t\t// Previously, `originalEvent: {}` was set here, so stopPropagation call\n\t\t\t\t// would not be triggered on donor event, since in our own\n\t\t\t\t// jQuery.event.stopPropagation function we had a check for existence of\n\t\t\t\t// originalEvent.stopPropagation method, so, consequently it would be a noop.\n\t\t\t\t//\n\t\t\t\t// But now, this \"simulate\" function is used only for events\n\t\t\t\t// for which stopPropagation() is noop, so there is no need for that anymore.\n\t\t\t\t//\n\t\t\t\t// For the compat branch though, guard for \"click\" and \"submit\"\n\t\t\t\t// events is still used, but was moved to jQuery.event.stopPropagation function\n\t\t\t\t// because `originalEvent` should point to the original event for the constancy\n\t\t\t\t// with other events and for more focused logic\n\t\t\t}\n\t\t);\n\n\t\tjQuery.event.trigger( e, null, elem );\n\n\t\tif ( e.isDefaultPrevented() ) {\n\t\t\tevent.preventDefault();\n\t\t}\n\t}\n\n} );\n\njQuery.fn.extend( {\n\n\ttrigger: function( type, data ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.trigger( type, data, this );\n\t\t} );\n\t},\n\ttriggerHandler: function( type, data ) {\n\t\tvar elem = this[ 0 ];\n\t\tif ( elem ) {\n\t\t\treturn jQuery.event.trigger( type, data, elem, true );\n\t\t}\n\t}\n} );\n\n\njQuery.each( ( \"blur focus focusin focusout load resize scroll unload click dblclick \" +\n\t\"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave \" +\n\t\"change select submit keydown keypress keyup error contextmenu\" ).split( \" \" ),\n\tfunction( i, name ) {\n\n\t// Handle event binding\n\tjQuery.fn[ name ] = function( data, fn ) {\n\t\treturn arguments.length > 0 ?\n\t\t\tthis.on( name, null, data, fn ) :\n\t\t\tthis.trigger( name );\n\t};\n} );\n\njQuery.fn.extend( {\n\thover: function( fnOver, fnOut ) {\n\t\treturn this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );\n\t}\n} );\n\n\n\n\nsupport.focusin = \"onfocusin\" in window;\n\n\n// Support: Firefox\n// Firefox doesn't have focus(in | out) events\n// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787\n//\n// Support: Chrome, Safari\n// focus(in | out) events fire after focus & blur events,\n// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order\n// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857\nif ( !support.focusin ) {\n\tjQuery.each( { focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\n\t\t// Attach a single capturing handler on the document while someone wants focusin/focusout\n\t\tvar handler = function( event ) {\n\t\t\tjQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );\n\t\t};\n\n\t\tjQuery.event.special[ fix ] = {\n\t\t\tsetup: function() {\n\t\t\t\tvar doc = this.ownerDocument || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix );\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.addEventListener( orig, handler, true );\n\t\t\t\t}\n\t\t\t\tdataPriv.access( doc, fix, ( attaches || 0 ) + 1 );\n\t\t\t},\n\t\t\tteardown: function() {\n\t\t\t\tvar doc = this.ownerDocument || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix ) - 1;\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.removeEventListener( orig, handler, true );\n\t\t\t\t\tdataPriv.remove( doc, fix );\n\n\t\t\t\t} else {\n\t\t\t\t\tdataPriv.access( doc, fix, attaches );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t} );\n}\nvar location = window.location;\n\nvar nonce = jQuery.now();\n\nvar rquery = ( /\\?/ );\n\n\n\n// Support: Android 2.3\n// Workaround failure to string-cast null input\njQuery.parseJSON = function( data ) {\n\treturn JSON.parse( data + \"\" );\n};\n\n\n// Cross-browser xml parsing\njQuery.parseXML = function( data ) {\n\tvar xml;\n\tif ( !data || typeof data !== \"string\" ) {\n\t\treturn null;\n\t}\n\n\t// Support: IE9\n\ttry {\n\t\txml = ( new window.DOMParser() ).parseFromString( data, \"text/xml\" );\n\t} catch ( e ) {\n\t\txml = undefined;\n\t}\n\n\tif ( !xml || xml.getElementsByTagName( \"parsererror\" ).length ) {\n\t\tjQuery.error( \"Invalid XML: \" + data );\n\t}\n\treturn xml;\n};\n\n\nvar\n\trhash = /#.*$/,\n\trts = /([?&])_=[^&]*/,\n\trheaders = /^(.*?):[ \\t]*([^\\r\\n]*)$/mg,\n\n\t// #7653, #8125, #8152: local protocol detection\n\trlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,\n\trnoContent = /^(?:GET|HEAD)$/,\n\trprotocol = /^\\/\\//,\n\n\t/* Prefilters\n\t * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)\n\t * 2) These are called:\n\t *    - BEFORE asking for a transport\n\t *    - AFTER param serialization (s.data is a string if s.processData is true)\n\t * 3) key is the dataType\n\t * 4) the catchall symbol \"*\" can be used\n\t * 5) execution will start with transport dataType and THEN continue down to \"*\" if needed\n\t */\n\tprefilters = {},\n\n\t/* Transports bindings\n\t * 1) key is the dataType\n\t * 2) the catchall symbol \"*\" can be used\n\t * 3) selection will start with transport dataType and THEN go to \"*\" if needed\n\t */\n\ttransports = {},\n\n\t// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression\n\tallTypes = \"*/\".concat( \"*\" ),\n\n\t// Anchor tag for parsing the document origin\n\toriginAnchor = document.createElement( \"a\" );\n\toriginAnchor.href = location.href;\n\n// Base \"constructor\" for jQuery.ajaxPrefilter and jQuery.ajaxTransport\nfunction addToPrefiltersOrTransports( structure ) {\n\n\t// dataTypeExpression is optional and defaults to \"*\"\n\treturn function( dataTypeExpression, func ) {\n\n\t\tif ( typeof dataTypeExpression !== \"string\" ) {\n\t\t\tfunc = dataTypeExpression;\n\t\t\tdataTypeExpression = \"*\";\n\t\t}\n\n\t\tvar dataType,\n\t\t\ti = 0,\n\t\t\tdataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];\n\n\t\tif ( jQuery.isFunction( func ) ) {\n\n\t\t\t// For each dataType in the dataTypeExpression\n\t\t\twhile ( ( dataType = dataTypes[ i++ ] ) ) {\n\n\t\t\t\t// Prepend if requested\n\t\t\t\tif ( dataType[ 0 ] === \"+\" ) {\n\t\t\t\t\tdataType = dataType.slice( 1 ) || \"*\";\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );\n\n\t\t\t\t// Otherwise append\n\t\t\t\t} else {\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).push( func );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n// Base inspection function for prefilters and transports\nfunction inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {\n\n\tvar inspected = {},\n\t\tseekingTransport = ( structure === transports );\n\n\tfunction inspect( dataType ) {\n\t\tvar selected;\n\t\tinspected[ dataType ] = true;\n\t\tjQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {\n\t\t\tvar dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );\n\t\t\tif ( typeof dataTypeOrTransport === \"string\" &&\n\t\t\t\t!seekingTransport && !inspected[ dataTypeOrTransport ] ) {\n\n\t\t\t\toptions.dataTypes.unshift( dataTypeOrTransport );\n\t\t\t\tinspect( dataTypeOrTransport );\n\t\t\t\treturn false;\n\t\t\t} else if ( seekingTransport ) {\n\t\t\t\treturn !( selected = dataTypeOrTransport );\n\t\t\t}\n\t\t} );\n\t\treturn selected;\n\t}\n\n\treturn inspect( options.dataTypes[ 0 ] ) || !inspected[ \"*\" ] && inspect( \"*\" );\n}\n\n// A special extend for ajax options\n// that takes \"flat\" options (not to be deep extended)\n// Fixes #9887\nfunction ajaxExtend( target, src ) {\n\tvar key, deep,\n\t\tflatOptions = jQuery.ajaxSettings.flatOptions || {};\n\n\tfor ( key in src ) {\n\t\tif ( src[ key ] !== undefined ) {\n\t\t\t( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];\n\t\t}\n\t}\n\tif ( deep ) {\n\t\tjQuery.extend( true, target, deep );\n\t}\n\n\treturn target;\n}\n\n/* Handles responses to an ajax request:\n * - finds the right dataType (mediates between content-type and expected dataType)\n * - returns the corresponding response\n */\nfunction ajaxHandleResponses( s, jqXHR, responses ) {\n\n\tvar ct, type, finalDataType, firstDataType,\n\t\tcontents = s.contents,\n\t\tdataTypes = s.dataTypes;\n\n\t// Remove auto dataType and get content-type in the process\n\twhile ( dataTypes[ 0 ] === \"*\" ) {\n\t\tdataTypes.shift();\n\t\tif ( ct === undefined ) {\n\t\t\tct = s.mimeType || jqXHR.getResponseHeader( \"Content-Type\" );\n\t\t}\n\t}\n\n\t// Check if we're dealing with a known content-type\n\tif ( ct ) {\n\t\tfor ( type in contents ) {\n\t\t\tif ( contents[ type ] && contents[ type ].test( ct ) ) {\n\t\t\t\tdataTypes.unshift( type );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check to see if we have a response for the expected dataType\n\tif ( dataTypes[ 0 ] in responses ) {\n\t\tfinalDataType = dataTypes[ 0 ];\n\t} else {\n\n\t\t// Try convertible dataTypes\n\t\tfor ( type in responses ) {\n\t\t\tif ( !dataTypes[ 0 ] || s.converters[ type + \" \" + dataTypes[ 0 ] ] ) {\n\t\t\t\tfinalDataType = type;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( !firstDataType ) {\n\t\t\t\tfirstDataType = type;\n\t\t\t}\n\t\t}\n\n\t\t// Or just use first one\n\t\tfinalDataType = finalDataType || firstDataType;\n\t}\n\n\t// If we found a dataType\n\t// We add the dataType to the list if needed\n\t// and return the corresponding response\n\tif ( finalDataType ) {\n\t\tif ( finalDataType !== dataTypes[ 0 ] ) {\n\t\t\tdataTypes.unshift( finalDataType );\n\t\t}\n\t\treturn responses[ finalDataType ];\n\t}\n}\n\n/* Chain conversions given the request and the original response\n * Also sets the responseXXX fields on the jqXHR instance\n */\nfunction ajaxConvert( s, response, jqXHR, isSuccess ) {\n\tvar conv2, current, conv, tmp, prev,\n\t\tconverters = {},\n\n\t\t// Work with a copy of dataTypes in case we need to modify it for conversion\n\t\tdataTypes = s.dataTypes.slice();\n\n\t// Create converters map with lowercased keys\n\tif ( dataTypes[ 1 ] ) {\n\t\tfor ( conv in s.converters ) {\n\t\t\tconverters[ conv.toLowerCase() ] = s.converters[ conv ];\n\t\t}\n\t}\n\n\tcurrent = dataTypes.shift();\n\n\t// Convert to each sequential dataType\n\twhile ( current ) {\n\n\t\tif ( s.responseFields[ current ] ) {\n\t\t\tjqXHR[ s.responseFields[ current ] ] = response;\n\t\t}\n\n\t\t// Apply the dataFilter if provided\n\t\tif ( !prev && isSuccess && s.dataFilter ) {\n\t\t\tresponse = s.dataFilter( response, s.dataType );\n\t\t}\n\n\t\tprev = current;\n\t\tcurrent = dataTypes.shift();\n\n\t\tif ( current ) {\n\n\t\t// There's only work to do if current dataType is non-auto\n\t\t\tif ( current === \"*\" ) {\n\n\t\t\t\tcurrent = prev;\n\n\t\t\t// Convert response if prev dataType is non-auto and differs from current\n\t\t\t} else if ( prev !== \"*\" && prev !== current ) {\n\n\t\t\t\t// Seek a direct converter\n\t\t\t\tconv = converters[ prev + \" \" + current ] || converters[ \"* \" + current ];\n\n\t\t\t\t// If none found, seek a pair\n\t\t\t\tif ( !conv ) {\n\t\t\t\t\tfor ( conv2 in converters ) {\n\n\t\t\t\t\t\t// If conv2 outputs current\n\t\t\t\t\t\ttmp = conv2.split( \" \" );\n\t\t\t\t\t\tif ( tmp[ 1 ] === current ) {\n\n\t\t\t\t\t\t\t// If prev can be converted to accepted input\n\t\t\t\t\t\t\tconv = converters[ prev + \" \" + tmp[ 0 ] ] ||\n\t\t\t\t\t\t\t\tconverters[ \"* \" + tmp[ 0 ] ];\n\t\t\t\t\t\t\tif ( conv ) {\n\n\t\t\t\t\t\t\t\t// Condense equivalence converters\n\t\t\t\t\t\t\t\tif ( conv === true ) {\n\t\t\t\t\t\t\t\t\tconv = converters[ conv2 ];\n\n\t\t\t\t\t\t\t\t// Otherwise, insert the intermediate dataType\n\t\t\t\t\t\t\t\t} else if ( converters[ conv2 ] !== true ) {\n\t\t\t\t\t\t\t\t\tcurrent = tmp[ 0 ];\n\t\t\t\t\t\t\t\t\tdataTypes.unshift( tmp[ 1 ] );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply converter (if not an equivalence)\n\t\t\t\tif ( conv !== true ) {\n\n\t\t\t\t\t// Unless errors are allowed to bubble, catch and return them\n\t\t\t\t\tif ( conv && s.throws ) {\n\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t\t} catch ( e ) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tstate: \"parsererror\",\n\t\t\t\t\t\t\t\terror: conv ? e : \"No conversion from \" + prev + \" to \" + current\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { state: \"success\", data: response };\n}\n\njQuery.extend( {\n\n\t// Counter for holding the number of active queries\n\tactive: 0,\n\n\t// Last-Modified header cache for next request\n\tlastModified: {},\n\tetag: {},\n\n\tajaxSettings: {\n\t\turl: location.href,\n\t\ttype: \"GET\",\n\t\tisLocal: rlocalProtocol.test( location.protocol ),\n\t\tglobal: true,\n\t\tprocessData: true,\n\t\tasync: true,\n\t\tcontentType: \"application/x-www-form-urlencoded; charset=UTF-8\",\n\t\t/*\n\t\ttimeout: 0,\n\t\tdata: null,\n\t\tdataType: null,\n\t\tusername: null,\n\t\tpassword: null,\n\t\tcache: null,\n\t\tthrows: false,\n\t\ttraditional: false,\n\t\theaders: {},\n\t\t*/\n\n\t\taccepts: {\n\t\t\t\"*\": allTypes,\n\t\t\ttext: \"text/plain\",\n\t\t\thtml: \"text/html\",\n\t\t\txml: \"application/xml, text/xml\",\n\t\t\tjson: \"application/json, text/javascript\"\n\t\t},\n\n\t\tcontents: {\n\t\t\txml: /\\bxml\\b/,\n\t\t\thtml: /\\bhtml/,\n\t\t\tjson: /\\bjson\\b/\n\t\t},\n\n\t\tresponseFields: {\n\t\t\txml: \"responseXML\",\n\t\t\ttext: \"responseText\",\n\t\t\tjson: \"responseJSON\"\n\t\t},\n\n\t\t// Data converters\n\t\t// Keys separate source (or catchall \"*\") and destination types with a single space\n\t\tconverters: {\n\n\t\t\t// Convert anything to text\n\t\t\t\"* text\": String,\n\n\t\t\t// Text to html (true = no transformation)\n\t\t\t\"text html\": true,\n\n\t\t\t// Evaluate text as a json expression\n\t\t\t\"text json\": jQuery.parseJSON,\n\n\t\t\t// Parse text as xml\n\t\t\t\"text xml\": jQuery.parseXML\n\t\t},\n\n\t\t// For options that shouldn't be deep extended:\n\t\t// you can add your own custom options here if\n\t\t// and when you create one that shouldn't be\n\t\t// deep extended (see ajaxExtend)\n\t\tflatOptions: {\n\t\t\turl: true,\n\t\t\tcontext: true\n\t\t}\n\t},\n\n\t// Creates a full fledged settings object into target\n\t// with both ajaxSettings and settings fields.\n\t// If target is omitted, writes into ajaxSettings.\n\tajaxSetup: function( target, settings ) {\n\t\treturn settings ?\n\n\t\t\t// Building a settings object\n\t\t\tajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :\n\n\t\t\t// Extending ajaxSettings\n\t\t\tajaxExtend( jQuery.ajaxSettings, target );\n\t},\n\n\tajaxPrefilter: addToPrefiltersOrTransports( prefilters ),\n\tajaxTransport: addToPrefiltersOrTransports( transports ),\n\n\t// Main method\n\tajax: function( url, options ) {\n\n\t\t// If url is an object, simulate pre-1.5 signature\n\t\tif ( typeof url === \"object\" ) {\n\t\t\toptions = url;\n\t\t\turl = undefined;\n\t\t}\n\n\t\t// Force options to be an object\n\t\toptions = options || {};\n\n\t\tvar transport,\n\n\t\t\t// URL without anti-cache param\n\t\t\tcacheURL,\n\n\t\t\t// Response headers\n\t\t\tresponseHeadersString,\n\t\t\tresponseHeaders,\n\n\t\t\t// timeout handle\n\t\t\ttimeoutTimer,\n\n\t\t\t// Url cleanup var\n\t\t\turlAnchor,\n\n\t\t\t// To know if global events are to be dispatched\n\t\t\tfireGlobals,\n\n\t\t\t// Loop variable\n\t\t\ti,\n\n\t\t\t// Create the final options object\n\t\t\ts = jQuery.ajaxSetup( {}, options ),\n\n\t\t\t// Callbacks context\n\t\t\tcallbackContext = s.context || s,\n\n\t\t\t// Context for global events is callbackContext if it is a DOM node or jQuery collection\n\t\t\tglobalEventContext = s.context &&\n\t\t\t\t( callbackContext.nodeType || callbackContext.jquery ) ?\n\t\t\t\t\tjQuery( callbackContext ) :\n\t\t\t\t\tjQuery.event,\n\n\t\t\t// Deferreds\n\t\t\tdeferred = jQuery.Deferred(),\n\t\t\tcompleteDeferred = jQuery.Callbacks( \"once memory\" ),\n\n\t\t\t// Status-dependent callbacks\n\t\t\tstatusCode = s.statusCode || {},\n\n\t\t\t// Headers (they are sent all at once)\n\t\t\trequestHeaders = {},\n\t\t\trequestHeadersNames = {},\n\n\t\t\t// The jqXHR state\n\t\t\tstate = 0,\n\n\t\t\t// Default abort message\n\t\t\tstrAbort = \"canceled\",\n\n\t\t\t// Fake xhr\n\t\t\tjqXHR = {\n\t\t\t\treadyState: 0,\n\n\t\t\t\t// Builds headers hashtable if needed\n\t\t\t\tgetResponseHeader: function( key ) {\n\t\t\t\t\tvar match;\n\t\t\t\t\tif ( state === 2 ) {\n\t\t\t\t\t\tif ( !responseHeaders ) {\n\t\t\t\t\t\t\tresponseHeaders = {};\n\t\t\t\t\t\t\twhile ( ( match = rheaders.exec( responseHeadersString ) ) ) {\n\t\t\t\t\t\t\t\tresponseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmatch = responseHeaders[ key.toLowerCase() ];\n\t\t\t\t\t}\n\t\t\t\t\treturn match == null ? null : match;\n\t\t\t\t},\n\n\t\t\t\t// Raw string\n\t\t\t\tgetAllResponseHeaders: function() {\n\t\t\t\t\treturn state === 2 ? responseHeadersString : null;\n\t\t\t\t},\n\n\t\t\t\t// Caches the header\n\t\t\t\tsetRequestHeader: function( name, value ) {\n\t\t\t\t\tvar lname = name.toLowerCase();\n\t\t\t\t\tif ( !state ) {\n\t\t\t\t\t\tname = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;\n\t\t\t\t\t\trequestHeaders[ name ] = value;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Overrides response content-type header\n\t\t\t\toverrideMimeType: function( type ) {\n\t\t\t\t\tif ( !state ) {\n\t\t\t\t\t\ts.mimeType = type;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Status-dependent callbacks\n\t\t\t\tstatusCode: function( map ) {\n\t\t\t\t\tvar code;\n\t\t\t\t\tif ( map ) {\n\t\t\t\t\t\tif ( state < 2 ) {\n\t\t\t\t\t\t\tfor ( code in map ) {\n\n\t\t\t\t\t\t\t\t// Lazy-add the new callback in a way that preserves old ones\n\t\t\t\t\t\t\t\tstatusCode[ code ] = [ statusCode[ code ], map[ code ] ];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Execute the appropriate callbacks\n\t\t\t\t\t\t\tjqXHR.always( map[ jqXHR.status ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Cancel the request\n\t\t\t\tabort: function( statusText ) {\n\t\t\t\t\tvar finalText = statusText || strAbort;\n\t\t\t\t\tif ( transport ) {\n\t\t\t\t\t\ttransport.abort( finalText );\n\t\t\t\t\t}\n\t\t\t\t\tdone( 0, finalText );\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t};\n\n\t\t// Attach deferreds\n\t\tdeferred.promise( jqXHR ).complete = completeDeferred.add;\n\t\tjqXHR.success = jqXHR.done;\n\t\tjqXHR.error = jqXHR.fail;\n\n\t\t// Remove hash character (#7531: and string promotion)\n\t\t// Add protocol if not provided (prefilters might expect it)\n\t\t// Handle falsy url in the settings object (#10093: consistency with old signature)\n\t\t// We also use the url parameter if available\n\t\ts.url = ( ( url || s.url || location.href ) + \"\" ).replace( rhash, \"\" )\n\t\t\t.replace( rprotocol, location.protocol + \"//\" );\n\n\t\t// Alias method option to type as per ticket #12004\n\t\ts.type = options.method || options.type || s.method || s.type;\n\n\t\t// Extract dataTypes list\n\t\ts.dataTypes = jQuery.trim( s.dataType || \"*\" ).toLowerCase().match( rnotwhite ) || [ \"\" ];\n\n\t\t// A cross-domain request is in order when the origin doesn't match the current origin.\n\t\tif ( s.crossDomain == null ) {\n\t\t\turlAnchor = document.createElement( \"a\" );\n\n\t\t\t// Support: IE8-11+\n\t\t\t// IE throws exception if url is malformed, e.g. http://example.com:80x/\n\t\t\ttry {\n\t\t\t\turlAnchor.href = s.url;\n\n\t\t\t\t// Support: IE8-11+\n\t\t\t\t// Anchor's host property isn't correctly set when s.url is relative\n\t\t\t\turlAnchor.href = urlAnchor.href;\n\t\t\t\ts.crossDomain = originAnchor.protocol + \"//\" + originAnchor.host !==\n\t\t\t\t\turlAnchor.protocol + \"//\" + urlAnchor.host;\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// If there is an error parsing the URL, assume it is crossDomain,\n\t\t\t\t// it can be rejected by the transport if it is invalid\n\t\t\t\ts.crossDomain = true;\n\t\t\t}\n\t\t}\n\n\t\t// Convert data if not already a string\n\t\tif ( s.data && s.processData && typeof s.data !== \"string\" ) {\n\t\t\ts.data = jQuery.param( s.data, s.traditional );\n\t\t}\n\n\t\t// Apply prefilters\n\t\tinspectPrefiltersOrTransports( prefilters, s, options, jqXHR );\n\n\t\t// If request was aborted inside a prefilter, stop there\n\t\tif ( state === 2 ) {\n\t\t\treturn jqXHR;\n\t\t}\n\n\t\t// We can fire global events as of now if asked to\n\t\t// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)\n\t\tfireGlobals = jQuery.event && s.global;\n\n\t\t// Watch for a new set of requests\n\t\tif ( fireGlobals && jQuery.active++ === 0 ) {\n\t\t\tjQuery.event.trigger( \"ajaxStart\" );\n\t\t}\n\n\t\t// Uppercase the type\n\t\ts.type = s.type.toUpperCase();\n\n\t\t// Determine if request has content\n\t\ts.hasContent = !rnoContent.test( s.type );\n\n\t\t// Save the URL in case we're toying with the If-Modified-Since\n\t\t// and/or If-None-Match header later on\n\t\tcacheURL = s.url;\n\n\t\t// More options handling for requests with no content\n\t\tif ( !s.hasContent ) {\n\n\t\t\t// If data is available, append data to url\n\t\t\tif ( s.data ) {\n\t\t\t\tcacheURL = ( s.url += ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + s.data );\n\n\t\t\t\t// #9682: remove data so that it's not used in an eventual retry\n\t\t\t\tdelete s.data;\n\t\t\t}\n\n\t\t\t// Add anti-cache in url if needed\n\t\t\tif ( s.cache === false ) {\n\t\t\t\ts.url = rts.test( cacheURL ) ?\n\n\t\t\t\t\t// If there is already a '_' parameter, set its value\n\t\t\t\t\tcacheURL.replace( rts, \"$1_=\" + nonce++ ) :\n\n\t\t\t\t\t// Otherwise add one to the end\n\t\t\t\t\tcacheURL + ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + \"_=\" + nonce++;\n\t\t\t}\n\t\t}\n\n\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\tif ( s.ifModified ) {\n\t\t\tif ( jQuery.lastModified[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-Modified-Since\", jQuery.lastModified[ cacheURL ] );\n\t\t\t}\n\t\t\tif ( jQuery.etag[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-None-Match\", jQuery.etag[ cacheURL ] );\n\t\t\t}\n\t\t}\n\n\t\t// Set the correct header, if data is being sent\n\t\tif ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {\n\t\t\tjqXHR.setRequestHeader( \"Content-Type\", s.contentType );\n\t\t}\n\n\t\t// Set the Accepts header for the server, depending on the dataType\n\t\tjqXHR.setRequestHeader(\n\t\t\t\"Accept\",\n\t\t\ts.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?\n\t\t\t\ts.accepts[ s.dataTypes[ 0 ] ] +\n\t\t\t\t\t( s.dataTypes[ 0 ] !== \"*\" ? \", \" + allTypes + \"; q=0.01\" : \"\" ) :\n\t\t\t\ts.accepts[ \"*\" ]\n\t\t);\n\n\t\t// Check for headers option\n\t\tfor ( i in s.headers ) {\n\t\t\tjqXHR.setRequestHeader( i, s.headers[ i ] );\n\t\t}\n\n\t\t// Allow custom headers/mimetypes and early abort\n\t\tif ( s.beforeSend &&\n\t\t\t( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {\n\n\t\t\t// Abort if not done already and return\n\t\t\treturn jqXHR.abort();\n\t\t}\n\n\t\t// Aborting is no longer a cancellation\n\t\tstrAbort = \"abort\";\n\n\t\t// Install callbacks on deferreds\n\t\tfor ( i in { success: 1, error: 1, complete: 1 } ) {\n\t\t\tjqXHR[ i ]( s[ i ] );\n\t\t}\n\n\t\t// Get transport\n\t\ttransport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );\n\n\t\t// If no transport, we auto-abort\n\t\tif ( !transport ) {\n\t\t\tdone( -1, \"No Transport\" );\n\t\t} else {\n\t\t\tjqXHR.readyState = 1;\n\n\t\t\t// Send global event\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxSend\", [ jqXHR, s ] );\n\t\t\t}\n\n\t\t\t// If request was aborted inside ajaxSend, stop there\n\t\t\tif ( state === 2 ) {\n\t\t\t\treturn jqXHR;\n\t\t\t}\n\n\t\t\t// Timeout\n\t\t\tif ( s.async && s.timeout > 0 ) {\n\t\t\t\ttimeoutTimer = window.setTimeout( function() {\n\t\t\t\t\tjqXHR.abort( \"timeout\" );\n\t\t\t\t}, s.timeout );\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tstate = 1;\n\t\t\t\ttransport.send( requestHeaders, done );\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// Propagate exception as error if not done\n\t\t\t\tif ( state < 2 ) {\n\t\t\t\t\tdone( -1, e );\n\n\t\t\t\t// Simply rethrow otherwise\n\t\t\t\t} else {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Callback for when everything is done\n\t\tfunction done( status, nativeStatusText, responses, headers ) {\n\t\t\tvar isSuccess, success, error, response, modified,\n\t\t\t\tstatusText = nativeStatusText;\n\n\t\t\t// Called once\n\t\t\tif ( state === 2 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// State is \"done\" now\n\t\t\tstate = 2;\n\n\t\t\t// Clear timeout if it exists\n\t\t\tif ( timeoutTimer ) {\n\t\t\t\twindow.clearTimeout( timeoutTimer );\n\t\t\t}\n\n\t\t\t// Dereference transport for early garbage collection\n\t\t\t// (no matter how long the jqXHR object will be used)\n\t\t\ttransport = undefined;\n\n\t\t\t// Cache response headers\n\t\t\tresponseHeadersString = headers || \"\";\n\n\t\t\t// Set readyState\n\t\t\tjqXHR.readyState = status > 0 ? 4 : 0;\n\n\t\t\t// Determine if successful\n\t\t\tisSuccess = status >= 200 && status < 300 || status === 304;\n\n\t\t\t// Get response data\n\t\t\tif ( responses ) {\n\t\t\t\tresponse = ajaxHandleResponses( s, jqXHR, responses );\n\t\t\t}\n\n\t\t\t// Convert no matter what (that way responseXXX fields are always set)\n\t\t\tresponse = ajaxConvert( s, response, jqXHR, isSuccess );\n\n\t\t\t// If successful, handle type chaining\n\t\t\tif ( isSuccess ) {\n\n\t\t\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\t\t\tif ( s.ifModified ) {\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"Last-Modified\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.lastModified[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"etag\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.etag[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if no content\n\t\t\t\tif ( status === 204 || s.type === \"HEAD\" ) {\n\t\t\t\t\tstatusText = \"nocontent\";\n\n\t\t\t\t// if not modified\n\t\t\t\t} else if ( status === 304 ) {\n\t\t\t\t\tstatusText = \"notmodified\";\n\n\t\t\t\t// If we have data, let's convert it\n\t\t\t\t} else {\n\t\t\t\t\tstatusText = response.state;\n\t\t\t\t\tsuccess = response.data;\n\t\t\t\t\terror = response.error;\n\t\t\t\t\tisSuccess = !error;\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Extract error from statusText and normalize for non-aborts\n\t\t\t\terror = statusText;\n\t\t\t\tif ( status || !statusText ) {\n\t\t\t\t\tstatusText = \"error\";\n\t\t\t\t\tif ( status < 0 ) {\n\t\t\t\t\t\tstatus = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set data for the fake xhr object\n\t\t\tjqXHR.status = status;\n\t\t\tjqXHR.statusText = ( nativeStatusText || statusText ) + \"\";\n\n\t\t\t// Success/Error\n\t\t\tif ( isSuccess ) {\n\t\t\t\tdeferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );\n\t\t\t} else {\n\t\t\t\tdeferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );\n\t\t\t}\n\n\t\t\t// Status-dependent callbacks\n\t\t\tjqXHR.statusCode( statusCode );\n\t\t\tstatusCode = undefined;\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( isSuccess ? \"ajaxSuccess\" : \"ajaxError\",\n\t\t\t\t\t[ jqXHR, s, isSuccess ? success : error ] );\n\t\t\t}\n\n\t\t\t// Complete\n\t\t\tcompleteDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxComplete\", [ jqXHR, s ] );\n\n\t\t\t\t// Handle the global AJAX counter\n\t\t\t\tif ( !( --jQuery.active ) ) {\n\t\t\t\t\tjQuery.event.trigger( \"ajaxStop\" );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jqXHR;\n\t},\n\n\tgetJSON: function( url, data, callback ) {\n\t\treturn jQuery.get( url, data, callback, \"json\" );\n\t},\n\n\tgetScript: function( url, callback ) {\n\t\treturn jQuery.get( url, undefined, callback, \"script\" );\n\t}\n} );\n\njQuery.each( [ \"get\", \"post\" ], function( i, method ) {\n\tjQuery[ method ] = function( url, data, callback, type ) {\n\n\t\t// Shift arguments if data argument was omitted\n\t\tif ( jQuery.isFunction( data ) ) {\n\t\t\ttype = type || callback;\n\t\t\tcallback = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\t// The url can be an options object (which then must have .url)\n\t\treturn jQuery.ajax( jQuery.extend( {\n\t\t\turl: url,\n\t\t\ttype: method,\n\t\t\tdataType: type,\n\t\t\tdata: data,\n\t\t\tsuccess: callback\n\t\t}, jQuery.isPlainObject( url ) && url ) );\n\t};\n} );\n\n\njQuery._evalUrl = function( url ) {\n\treturn jQuery.ajax( {\n\t\turl: url,\n\n\t\t// Make this explicit, since user can override this through ajaxSetup (#11264)\n\t\ttype: \"GET\",\n\t\tdataType: \"script\",\n\t\tasync: false,\n\t\tglobal: false,\n\t\t\"throws\": true\n\t} );\n};\n\n\njQuery.fn.extend( {\n\twrapAll: function( html ) {\n\t\tvar wrap;\n\n\t\tif ( jQuery.isFunction( html ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).wrapAll( html.call( this, i ) );\n\t\t\t} );\n\t\t}\n\n\t\tif ( this[ 0 ] ) {\n\n\t\t\t// The elements to wrap the target around\n\t\t\twrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );\n\n\t\t\tif ( this[ 0 ].parentNode ) {\n\t\t\t\twrap.insertBefore( this[ 0 ] );\n\t\t\t}\n\n\t\t\twrap.map( function() {\n\t\t\t\tvar elem = this;\n\n\t\t\t\twhile ( elem.firstElementChild ) {\n\t\t\t\t\telem = elem.firstElementChild;\n\t\t\t\t}\n\n\t\t\t\treturn elem;\n\t\t\t} ).append( this );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\twrapInner: function( html ) {\n\t\tif ( jQuery.isFunction( html ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).wrapInner( html.call( this, i ) );\n\t\t\t} );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar self = jQuery( this ),\n\t\t\t\tcontents = self.contents();\n\n\t\t\tif ( contents.length ) {\n\t\t\t\tcontents.wrapAll( html );\n\n\t\t\t} else {\n\t\t\t\tself.append( html );\n\t\t\t}\n\t\t} );\n\t},\n\n\twrap: function( html ) {\n\t\tvar isFunction = jQuery.isFunction( html );\n\n\t\treturn this.each( function( i ) {\n\t\t\tjQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html );\n\t\t} );\n\t},\n\n\tunwrap: function() {\n\t\treturn this.parent().each( function() {\n\t\t\tif ( !jQuery.nodeName( this, \"body\" ) ) {\n\t\t\t\tjQuery( this ).replaceWith( this.childNodes );\n\t\t\t}\n\t\t} ).end();\n\t}\n} );\n\n\njQuery.expr.filters.hidden = function( elem ) {\n\treturn !jQuery.expr.filters.visible( elem );\n};\njQuery.expr.filters.visible = function( elem ) {\n\n\t// Support: Opera <= 12.12\n\t// Opera reports offsetWidths and offsetHeights less than zero on some elements\n\t// Use OR instead of AND as the element is not visible if either is true\n\t// See tickets #10406 and #13132\n\treturn elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0;\n};\n\n\n\n\nvar r20 = /%20/g,\n\trbracket = /\\[\\]$/,\n\trCRLF = /\\r?\\n/g,\n\trsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,\n\trsubmittable = /^(?:input|select|textarea|keygen)/i;\n\nfunction buildParams( prefix, obj, traditional, add ) {\n\tvar name;\n\n\tif ( jQuery.isArray( obj ) ) {\n\n\t\t// Serialize array item.\n\t\tjQuery.each( obj, function( i, v ) {\n\t\t\tif ( traditional || rbracket.test( prefix ) ) {\n\n\t\t\t\t// Treat each array item as a scalar.\n\t\t\t\tadd( prefix, v );\n\n\t\t\t} else {\n\n\t\t\t\t// Item is non-scalar (array or object), encode its numeric index.\n\t\t\t\tbuildParams(\n\t\t\t\t\tprefix + \"[\" + ( typeof v === \"object\" && v != null ? i : \"\" ) + \"]\",\n\t\t\t\t\tv,\n\t\t\t\t\ttraditional,\n\t\t\t\t\tadd\n\t\t\t\t);\n\t\t\t}\n\t\t} );\n\n\t} else if ( !traditional && jQuery.type( obj ) === \"object\" ) {\n\n\t\t// Serialize object item.\n\t\tfor ( name in obj ) {\n\t\t\tbuildParams( prefix + \"[\" + name + \"]\", obj[ name ], traditional, add );\n\t\t}\n\n\t} else {\n\n\t\t// Serialize scalar item.\n\t\tadd( prefix, obj );\n\t}\n}\n\n// Serialize an array of form elements or a set of\n// key/values into a query string\njQuery.param = function( a, traditional ) {\n\tvar prefix,\n\t\ts = [],\n\t\tadd = function( key, value ) {\n\n\t\t\t// If value is a function, invoke it and return its value\n\t\t\tvalue = jQuery.isFunction( value ) ? value() : ( value == null ? \"\" : value );\n\t\t\ts[ s.length ] = encodeURIComponent( key ) + \"=\" + encodeURIComponent( value );\n\t\t};\n\n\t// Set traditional to true for jQuery <= 1.3.2 behavior.\n\tif ( traditional === undefined ) {\n\t\ttraditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;\n\t}\n\n\t// If an array was passed in, assume that it is an array of form elements.\n\tif ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {\n\n\t\t// Serialize the form elements\n\t\tjQuery.each( a, function() {\n\t\t\tadd( this.name, this.value );\n\t\t} );\n\n\t} else {\n\n\t\t// If traditional, encode the \"old\" way (the way 1.3.2 or older\n\t\t// did it), otherwise encode params recursively.\n\t\tfor ( prefix in a ) {\n\t\t\tbuildParams( prefix, a[ prefix ], traditional, add );\n\t\t}\n\t}\n\n\t// Return the resulting serialization\n\treturn s.join( \"&\" ).replace( r20, \"+\" );\n};\n\njQuery.fn.extend( {\n\tserialize: function() {\n\t\treturn jQuery.param( this.serializeArray() );\n\t},\n\tserializeArray: function() {\n\t\treturn this.map( function() {\n\n\t\t\t// Can add propHook for \"elements\" to filter or add form elements\n\t\t\tvar elements = jQuery.prop( this, \"elements\" );\n\t\t\treturn elements ? jQuery.makeArray( elements ) : this;\n\t\t} )\n\t\t.filter( function() {\n\t\t\tvar type = this.type;\n\n\t\t\t// Use .is( \":disabled\" ) so that fieldset[disabled] works\n\t\t\treturn this.name && !jQuery( this ).is( \":disabled\" ) &&\n\t\t\t\trsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&\n\t\t\t\t( this.checked || !rcheckableType.test( type ) );\n\t\t} )\n\t\t.map( function( i, elem ) {\n\t\t\tvar val = jQuery( this ).val();\n\n\t\t\treturn val == null ?\n\t\t\t\tnull :\n\t\t\t\tjQuery.isArray( val ) ?\n\t\t\t\t\tjQuery.map( val, function( val ) {\n\t\t\t\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t\t\t\t} ) :\n\t\t\t\t\t{ name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t} ).get();\n\t}\n} );\n\n\njQuery.ajaxSettings.xhr = function() {\n\ttry {\n\t\treturn new window.XMLHttpRequest();\n\t} catch ( e ) {}\n};\n\nvar xhrSuccessStatus = {\n\n\t\t// File protocol always yields status code 0, assume 200\n\t\t0: 200,\n\n\t\t// Support: IE9\n\t\t// #1450: sometimes IE returns 1223 when it should be 204\n\t\t1223: 204\n\t},\n\txhrSupported = jQuery.ajaxSettings.xhr();\n\nsupport.cors = !!xhrSupported && ( \"withCredentials\" in xhrSupported );\nsupport.ajax = xhrSupported = !!xhrSupported;\n\njQuery.ajaxTransport( function( options ) {\n\tvar callback, errorCallback;\n\n\t// Cross domain only allowed if supported through XMLHttpRequest\n\tif ( support.cors || xhrSupported && !options.crossDomain ) {\n\t\treturn {\n\t\t\tsend: function( headers, complete ) {\n\t\t\t\tvar i,\n\t\t\t\t\txhr = options.xhr();\n\n\t\t\t\txhr.open(\n\t\t\t\t\toptions.type,\n\t\t\t\t\toptions.url,\n\t\t\t\t\toptions.async,\n\t\t\t\t\toptions.username,\n\t\t\t\t\toptions.password\n\t\t\t\t);\n\n\t\t\t\t// Apply custom fields if provided\n\t\t\t\tif ( options.xhrFields ) {\n\t\t\t\t\tfor ( i in options.xhrFields ) {\n\t\t\t\t\t\txhr[ i ] = options.xhrFields[ i ];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Override mime type if needed\n\t\t\t\tif ( options.mimeType && xhr.overrideMimeType ) {\n\t\t\t\t\txhr.overrideMimeType( options.mimeType );\n\t\t\t\t}\n\n\t\t\t\t// X-Requested-With header\n\t\t\t\t// For cross-domain requests, seeing as conditions for a preflight are\n\t\t\t\t// akin to a jigsaw puzzle, we simply never set it to be sure.\n\t\t\t\t// (it can always be set on a per-request basis or even using ajaxSetup)\n\t\t\t\t// For same-domain requests, won't change header if already provided.\n\t\t\t\tif ( !options.crossDomain && !headers[ \"X-Requested-With\" ] ) {\n\t\t\t\t\theaders[ \"X-Requested-With\" ] = \"XMLHttpRequest\";\n\t\t\t\t}\n\n\t\t\t\t// Set headers\n\t\t\t\tfor ( i in headers ) {\n\t\t\t\t\txhr.setRequestHeader( i, headers[ i ] );\n\t\t\t\t}\n\n\t\t\t\t// Callback\n\t\t\t\tcallback = function( type ) {\n\t\t\t\t\treturn function() {\n\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\tcallback = errorCallback = xhr.onload =\n\t\t\t\t\t\t\t\txhr.onerror = xhr.onabort = xhr.onreadystatechange = null;\n\n\t\t\t\t\t\t\tif ( type === \"abort\" ) {\n\t\t\t\t\t\t\t\txhr.abort();\n\t\t\t\t\t\t\t} else if ( type === \"error\" ) {\n\n\t\t\t\t\t\t\t\t// Support: IE9\n\t\t\t\t\t\t\t\t// On a manual native abort, IE9 throws\n\t\t\t\t\t\t\t\t// errors on any property access that is not readyState\n\t\t\t\t\t\t\t\tif ( typeof xhr.status !== \"number\" ) {\n\t\t\t\t\t\t\t\t\tcomplete( 0, \"error\" );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcomplete(\n\n\t\t\t\t\t\t\t\t\t\t// File: protocol always yields status 0; see #8605, #14207\n\t\t\t\t\t\t\t\t\t\txhr.status,\n\t\t\t\t\t\t\t\t\t\txhr.statusText\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcomplete(\n\t\t\t\t\t\t\t\t\txhrSuccessStatus[ xhr.status ] || xhr.status,\n\t\t\t\t\t\t\t\t\txhr.statusText,\n\n\t\t\t\t\t\t\t\t\t// Support: IE9 only\n\t\t\t\t\t\t\t\t\t// IE9 has no XHR2 but throws on binary (trac-11426)\n\t\t\t\t\t\t\t\t\t// For XHR2 non-text, let the caller handle it (gh-2498)\n\t\t\t\t\t\t\t\t\t( xhr.responseType || \"text\" ) !== \"text\"  ||\n\t\t\t\t\t\t\t\t\ttypeof xhr.responseText !== \"string\" ?\n\t\t\t\t\t\t\t\t\t\t{ binary: xhr.response } :\n\t\t\t\t\t\t\t\t\t\t{ text: xhr.responseText },\n\t\t\t\t\t\t\t\t\txhr.getAllResponseHeaders()\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t};\n\n\t\t\t\t// Listen to events\n\t\t\t\txhr.onload = callback();\n\t\t\t\terrorCallback = xhr.onerror = callback( \"error\" );\n\n\t\t\t\t// Support: IE9\n\t\t\t\t// Use onreadystatechange to replace onabort\n\t\t\t\t// to handle uncaught aborts\n\t\t\t\tif ( xhr.onabort !== undefined ) {\n\t\t\t\t\txhr.onabort = errorCallback;\n\t\t\t\t} else {\n\t\t\t\t\txhr.onreadystatechange = function() {\n\n\t\t\t\t\t\t// Check readyState before timeout as it changes\n\t\t\t\t\t\tif ( xhr.readyState === 4 ) {\n\n\t\t\t\t\t\t\t// Allow onerror to be called first,\n\t\t\t\t\t\t\t// but that will not handle a native abort\n\t\t\t\t\t\t\t// Also, save errorCallback to a variable\n\t\t\t\t\t\t\t// as xhr.onerror cannot be accessed\n\t\t\t\t\t\t\twindow.setTimeout( function() {\n\t\t\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\t\t\terrorCallback();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Create the abort callback\n\t\t\t\tcallback = callback( \"abort\" );\n\n\t\t\t\ttry {\n\n\t\t\t\t\t// Do send the request (this may raise an exception)\n\t\t\t\t\txhr.send( options.hasContent && options.data || null );\n\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t// #14683: Only rethrow if this hasn't been notified as an error yet\n\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tabort: function() {\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tcallback();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n} );\n\n\n\n\n// Install script dataType\njQuery.ajaxSetup( {\n\taccepts: {\n\t\tscript: \"text/javascript, application/javascript, \" +\n\t\t\t\"application/ecmascript, application/x-ecmascript\"\n\t},\n\tcontents: {\n\t\tscript: /\\b(?:java|ecma)script\\b/\n\t},\n\tconverters: {\n\t\t\"text script\": function( text ) {\n\t\t\tjQuery.globalEval( text );\n\t\t\treturn text;\n\t\t}\n\t}\n} );\n\n// Handle cache's special case and crossDomain\njQuery.ajaxPrefilter( \"script\", function( s ) {\n\tif ( s.cache === undefined ) {\n\t\ts.cache = false;\n\t}\n\tif ( s.crossDomain ) {\n\t\ts.type = \"GET\";\n\t}\n} );\n\n// Bind script tag hack transport\njQuery.ajaxTransport( \"script\", function( s ) {\n\n\t// This transport only deals with cross domain requests\n\tif ( s.crossDomain ) {\n\t\tvar script, callback;\n\t\treturn {\n\t\t\tsend: function( _, complete ) {\n\t\t\t\tscript = jQuery( \"<script>\" ).prop( {\n\t\t\t\t\tcharset: s.scriptCharset,\n\t\t\t\t\tsrc: s.url\n\t\t\t\t} ).on(\n\t\t\t\t\t\"load error\",\n\t\t\t\t\tcallback = function( evt ) {\n\t\t\t\t\t\tscript.remove();\n\t\t\t\t\t\tcallback = null;\n\t\t\t\t\t\tif ( evt ) {\n\t\t\t\t\t\t\tcomplete( evt.type === \"error\" ? 404 : 200, evt.type );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\t// Use native DOM manipulation to avoid our domManip AJAX trickery\n\t\t\t\tdocument.head.appendChild( script[ 0 ] );\n\t\t\t},\n\t\t\tabort: function() {\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tcallback();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n} );\n\n\n\n\nvar oldCallbacks = [],\n\trjsonp = /(=)\\?(?=&|$)|\\?\\?/;\n\n// Default jsonp settings\njQuery.ajaxSetup( {\n\tjsonp: \"callback\",\n\tjsonpCallback: function() {\n\t\tvar callback = oldCallbacks.pop() || ( jQuery.expando + \"_\" + ( nonce++ ) );\n\t\tthis[ callback ] = true;\n\t\treturn callback;\n\t}\n} );\n\n// Detect, normalize options and install callbacks for jsonp requests\njQuery.ajaxPrefilter( \"json jsonp\", function( s, originalSettings, jqXHR ) {\n\n\tvar callbackName, overwritten, responseContainer,\n\t\tjsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?\n\t\t\t\"url\" :\n\t\t\ttypeof s.data === \"string\" &&\n\t\t\t\t( s.contentType || \"\" )\n\t\t\t\t\t.indexOf( \"application/x-www-form-urlencoded\" ) === 0 &&\n\t\t\t\trjsonp.test( s.data ) && \"data\"\n\t\t);\n\n\t// Handle iff the expected data type is \"jsonp\" or we have a parameter to set\n\tif ( jsonProp || s.dataTypes[ 0 ] === \"jsonp\" ) {\n\n\t\t// Get callback name, remembering preexisting value associated with it\n\t\tcallbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?\n\t\t\ts.jsonpCallback() :\n\t\t\ts.jsonpCallback;\n\n\t\t// Insert callback into url or form data\n\t\tif ( jsonProp ) {\n\t\t\ts[ jsonProp ] = s[ jsonProp ].replace( rjsonp, \"$1\" + callbackName );\n\t\t} else if ( s.jsonp !== false ) {\n\t\t\ts.url += ( rquery.test( s.url ) ? \"&\" : \"?\" ) + s.jsonp + \"=\" + callbackName;\n\t\t}\n\n\t\t// Use data converter to retrieve json after script execution\n\t\ts.converters[ \"script json\" ] = function() {\n\t\t\tif ( !responseContainer ) {\n\t\t\t\tjQuery.error( callbackName + \" was not called\" );\n\t\t\t}\n\t\t\treturn responseContainer[ 0 ];\n\t\t};\n\n\t\t// Force json dataType\n\t\ts.dataTypes[ 0 ] = \"json\";\n\n\t\t// Install callback\n\t\toverwritten = window[ callbackName ];\n\t\twindow[ callbackName ] = function() {\n\t\t\tresponseContainer = arguments;\n\t\t};\n\n\t\t// Clean-up function (fires after converters)\n\t\tjqXHR.always( function() {\n\n\t\t\t// If previous value didn't exist - remove it\n\t\t\tif ( overwritten === undefined ) {\n\t\t\t\tjQuery( window ).removeProp( callbackName );\n\n\t\t\t// Otherwise restore preexisting value\n\t\t\t} else {\n\t\t\t\twindow[ callbackName ] = overwritten;\n\t\t\t}\n\n\t\t\t// Save back as free\n\t\t\tif ( s[ callbackName ] ) {\n\n\t\t\t\t// Make sure that re-using the options doesn't screw things around\n\t\t\t\ts.jsonpCallback = originalSettings.jsonpCallback;\n\n\t\t\t\t// Save the callback name for future use\n\t\t\t\toldCallbacks.push( callbackName );\n\t\t\t}\n\n\t\t\t// Call if it was a function and we have a response\n\t\t\tif ( responseContainer && jQuery.isFunction( overwritten ) ) {\n\t\t\t\toverwritten( responseContainer[ 0 ] );\n\t\t\t}\n\n\t\t\tresponseContainer = overwritten = undefined;\n\t\t} );\n\n\t\t// Delegate to script\n\t\treturn \"script\";\n\t}\n} );\n\n\n\n\n// Support: Safari 8+\n// In Safari 8 documents created via document.implementation.createHTMLDocument\n// collapse sibling forms: the second one becomes a child of the first one.\n// Because of that, this security measure has to be disabled in Safari 8.\n// https://bugs.webkit.org/show_bug.cgi?id=137337\nsupport.createHTMLDocument = ( function() {\n\tvar body = document.implementation.createHTMLDocument( \"\" ).body;\n\tbody.innerHTML = \"<form></form><form></form>\";\n\treturn body.childNodes.length === 2;\n} )();\n\n\n// Argument \"data\" should be string of html\n// context (optional): If specified, the fragment will be created in this context,\n// defaults to document\n// keepScripts (optional): If true, will include scripts passed in the html string\njQuery.parseHTML = function( data, context, keepScripts ) {\n\tif ( !data || typeof data !== \"string\" ) {\n\t\treturn null;\n\t}\n\tif ( typeof context === \"boolean\" ) {\n\t\tkeepScripts = context;\n\t\tcontext = false;\n\t}\n\n\t// Stop scripts or inline event handlers from being executed immediately\n\t// by using document.implementation\n\tcontext = context || ( support.createHTMLDocument ?\n\t\tdocument.implementation.createHTMLDocument( \"\" ) :\n\t\tdocument );\n\n\tvar parsed = rsingleTag.exec( data ),\n\t\tscripts = !keepScripts && [];\n\n\t// Single tag\n\tif ( parsed ) {\n\t\treturn [ context.createElement( parsed[ 1 ] ) ];\n\t}\n\n\tparsed = buildFragment( [ data ], context, scripts );\n\n\tif ( scripts && scripts.length ) {\n\t\tjQuery( scripts ).remove();\n\t}\n\n\treturn jQuery.merge( [], parsed.childNodes );\n};\n\n\n// Keep a copy of the old load method\nvar _load = jQuery.fn.load;\n\n/**\n * Load a url into a page\n */\njQuery.fn.load = function( url, params, callback ) {\n\tif ( typeof url !== \"string\" && _load ) {\n\t\treturn _load.apply( this, arguments );\n\t}\n\n\tvar selector, type, response,\n\t\tself = this,\n\t\toff = url.indexOf( \" \" );\n\n\tif ( off > -1 ) {\n\t\tselector = jQuery.trim( url.slice( off ) );\n\t\turl = url.slice( 0, off );\n\t}\n\n\t// If it's a function\n\tif ( jQuery.isFunction( params ) ) {\n\n\t\t// We assume that it's the callback\n\t\tcallback = params;\n\t\tparams = undefined;\n\n\t// Otherwise, build a param string\n\t} else if ( params && typeof params === \"object\" ) {\n\t\ttype = \"POST\";\n\t}\n\n\t// If we have elements to modify, make the request\n\tif ( self.length > 0 ) {\n\t\tjQuery.ajax( {\n\t\t\turl: url,\n\n\t\t\t// If \"type\" variable is undefined, then \"GET\" method will be used.\n\t\t\t// Make value of this field explicit since\n\t\t\t// user can override it through ajaxSetup method\n\t\t\ttype: type || \"GET\",\n\t\t\tdataType: \"html\",\n\t\t\tdata: params\n\t\t} ).done( function( responseText ) {\n\n\t\t\t// Save response for use in complete callback\n\t\t\tresponse = arguments;\n\n\t\t\tself.html( selector ?\n\n\t\t\t\t// If a selector was specified, locate the right elements in a dummy div\n\t\t\t\t// Exclude scripts to avoid IE 'Permission Denied' errors\n\t\t\t\tjQuery( \"<div>\" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :\n\n\t\t\t\t// Otherwise use the full result\n\t\t\t\tresponseText );\n\n\t\t// If the request succeeds, this function gets \"data\", \"status\", \"jqXHR\"\n\t\t// but they are ignored because response was set above.\n\t\t// If it fails, this function gets \"jqXHR\", \"status\", \"error\"\n\t\t} ).always( callback && function( jqXHR, status ) {\n\t\t\tself.each( function() {\n\t\t\t\tcallback.apply( self, response || [ jqXHR.responseText, status, jqXHR ] );\n\t\t\t} );\n\t\t} );\n\t}\n\n\treturn this;\n};\n\n\n\n\n// Attach a bunch of functions for handling common AJAX events\njQuery.each( [\n\t\"ajaxStart\",\n\t\"ajaxStop\",\n\t\"ajaxComplete\",\n\t\"ajaxError\",\n\t\"ajaxSuccess\",\n\t\"ajaxSend\"\n], function( i, type ) {\n\tjQuery.fn[ type ] = function( fn ) {\n\t\treturn this.on( type, fn );\n\t};\n} );\n\n\n\n\njQuery.expr.filters.animated = function( elem ) {\n\treturn jQuery.grep( jQuery.timers, function( fn ) {\n\t\treturn elem === fn.elem;\n\t} ).length;\n};\n\n\n\n\n/**\n * Gets a window from an element\n */\nfunction getWindow( elem ) {\n\treturn jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;\n}\n\njQuery.offset = {\n\tsetOffset: function( elem, options, i ) {\n\t\tvar curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,\n\t\t\tposition = jQuery.css( elem, \"position\" ),\n\t\t\tcurElem = jQuery( elem ),\n\t\t\tprops = {};\n\n\t\t// Set position first, in-case top/left are set even on static elem\n\t\tif ( position === \"static\" ) {\n\t\t\telem.style.position = \"relative\";\n\t\t}\n\n\t\tcurOffset = curElem.offset();\n\t\tcurCSSTop = jQuery.css( elem, \"top\" );\n\t\tcurCSSLeft = jQuery.css( elem, \"left\" );\n\t\tcalculatePosition = ( position === \"absolute\" || position === \"fixed\" ) &&\n\t\t\t( curCSSTop + curCSSLeft ).indexOf( \"auto\" ) > -1;\n\n\t\t// Need to be able to calculate position if either\n\t\t// top or left is auto and position is either absolute or fixed\n\t\tif ( calculatePosition ) {\n\t\t\tcurPosition = curElem.position();\n\t\t\tcurTop = curPosition.top;\n\t\t\tcurLeft = curPosition.left;\n\n\t\t} else {\n\t\t\tcurTop = parseFloat( curCSSTop ) || 0;\n\t\t\tcurLeft = parseFloat( curCSSLeft ) || 0;\n\t\t}\n\n\t\tif ( jQuery.isFunction( options ) ) {\n\n\t\t\t// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)\n\t\t\toptions = options.call( elem, i, jQuery.extend( {}, curOffset ) );\n\t\t}\n\n\t\tif ( options.top != null ) {\n\t\t\tprops.top = ( options.top - curOffset.top ) + curTop;\n\t\t}\n\t\tif ( options.left != null ) {\n\t\t\tprops.left = ( options.left - curOffset.left ) + curLeft;\n\t\t}\n\n\t\tif ( \"using\" in options ) {\n\t\t\toptions.using.call( elem, props );\n\n\t\t} else {\n\t\t\tcurElem.css( props );\n\t\t}\n\t}\n};\n\njQuery.fn.extend( {\n\toffset: function( options ) {\n\t\tif ( arguments.length ) {\n\t\t\treturn options === undefined ?\n\t\t\t\tthis :\n\t\t\t\tthis.each( function( i ) {\n\t\t\t\t\tjQuery.offset.setOffset( this, options, i );\n\t\t\t\t} );\n\t\t}\n\n\t\tvar docElem, win,\n\t\t\telem = this[ 0 ],\n\t\t\tbox = { top: 0, left: 0 },\n\t\t\tdoc = elem && elem.ownerDocument;\n\n\t\tif ( !doc ) {\n\t\t\treturn;\n\t\t}\n\n\t\tdocElem = doc.documentElement;\n\n\t\t// Make sure it's not a disconnected DOM node\n\t\tif ( !jQuery.contains( docElem, elem ) ) {\n\t\t\treturn box;\n\t\t}\n\n\t\tbox = elem.getBoundingClientRect();\n\t\twin = getWindow( doc );\n\t\treturn {\n\t\t\ttop: box.top + win.pageYOffset - docElem.clientTop,\n\t\t\tleft: box.left + win.pageXOffset - docElem.clientLeft\n\t\t};\n\t},\n\n\tposition: function() {\n\t\tif ( !this[ 0 ] ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar offsetParent, offset,\n\t\t\telem = this[ 0 ],\n\t\t\tparentOffset = { top: 0, left: 0 };\n\n\t\t// Fixed elements are offset from window (parentOffset = {top:0, left: 0},\n\t\t// because it is its only offset parent\n\t\tif ( jQuery.css( elem, \"position\" ) === \"fixed\" ) {\n\n\t\t\t// Assume getBoundingClientRect is there when computed position is fixed\n\t\t\toffset = elem.getBoundingClientRect();\n\n\t\t} else {\n\n\t\t\t// Get *real* offsetParent\n\t\t\toffsetParent = this.offsetParent();\n\n\t\t\t// Get correct offsets\n\t\t\toffset = this.offset();\n\t\t\tif ( !jQuery.nodeName( offsetParent[ 0 ], \"html\" ) ) {\n\t\t\t\tparentOffset = offsetParent.offset();\n\t\t\t}\n\n\t\t\t// Add offsetParent borders\n\t\t\t// Subtract offsetParent scroll positions\n\t\t\tparentOffset.top += jQuery.css( offsetParent[ 0 ], \"borderTopWidth\", true ) -\n\t\t\t\toffsetParent.scrollTop();\n\t\t\tparentOffset.left += jQuery.css( offsetParent[ 0 ], \"borderLeftWidth\", true ) -\n\t\t\t\toffsetParent.scrollLeft();\n\t\t}\n\n\t\t// Subtract parent offsets and element margins\n\t\treturn {\n\t\t\ttop: offset.top - parentOffset.top - jQuery.css( elem, \"marginTop\", true ),\n\t\t\tleft: offset.left - parentOffset.left - jQuery.css( elem, \"marginLeft\", true )\n\t\t};\n\t},\n\n\t// This method will return documentElement in the following cases:\n\t// 1) For the element inside the iframe without offsetParent, this method will return\n\t//    documentElement of the parent window\n\t// 2) For the hidden or detached element\n\t// 3) For body or html element, i.e. in case of the html node - it will return itself\n\t//\n\t// but those exceptions were never presented as a real life use-cases\n\t// and might be considered as more preferable results.\n\t//\n\t// This logic, however, is not guaranteed and can change at any point in the future\n\toffsetParent: function() {\n\t\treturn this.map( function() {\n\t\t\tvar offsetParent = this.offsetParent;\n\n\t\t\twhile ( offsetParent && jQuery.css( offsetParent, \"position\" ) === \"static\" ) {\n\t\t\t\toffsetParent = offsetParent.offsetParent;\n\t\t\t}\n\n\t\t\treturn offsetParent || documentElement;\n\t\t} );\n\t}\n} );\n\n// Create scrollLeft and scrollTop methods\njQuery.each( { scrollLeft: \"pageXOffset\", scrollTop: \"pageYOffset\" }, function( method, prop ) {\n\tvar top = \"pageYOffset\" === prop;\n\n\tjQuery.fn[ method ] = function( val ) {\n\t\treturn access( this, function( elem, method, val ) {\n\t\t\tvar win = getWindow( elem );\n\n\t\t\tif ( val === undefined ) {\n\t\t\t\treturn win ? win[ prop ] : elem[ method ];\n\t\t\t}\n\n\t\t\tif ( win ) {\n\t\t\t\twin.scrollTo(\n\t\t\t\t\t!top ? val : win.pageXOffset,\n\t\t\t\t\ttop ? val : win.pageYOffset\n\t\t\t\t);\n\n\t\t\t} else {\n\t\t\t\telem[ method ] = val;\n\t\t\t}\n\t\t}, method, val, arguments.length );\n\t};\n} );\n\n// Support: Safari<7-8+, Chrome<37-44+\n// Add the top/left cssHooks using jQuery.fn.position\n// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084\n// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280\n// getComputedStyle returns percent when specified for top/left/bottom/right;\n// rather than make the css module depend on the offset module, just check for it here\njQuery.each( [ \"top\", \"left\" ], function( i, prop ) {\n\tjQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,\n\t\tfunction( elem, computed ) {\n\t\t\tif ( computed ) {\n\t\t\t\tcomputed = curCSS( elem, prop );\n\n\t\t\t\t// If curCSS returns percentage, fallback to offset\n\t\t\t\treturn rnumnonpx.test( computed ) ?\n\t\t\t\t\tjQuery( elem ).position()[ prop ] + \"px\" :\n\t\t\t\t\tcomputed;\n\t\t\t}\n\t\t}\n\t);\n} );\n\n\n// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods\njQuery.each( { Height: \"height\", Width: \"width\" }, function( name, type ) {\n\tjQuery.each( { padding: \"inner\" + name, content: type, \"\": \"outer\" + name },\n\t\tfunction( defaultExtra, funcName ) {\n\n\t\t// Margin is only for outerHeight, outerWidth\n\t\tjQuery.fn[ funcName ] = function( margin, value ) {\n\t\t\tvar chainable = arguments.length && ( defaultExtra || typeof margin !== \"boolean\" ),\n\t\t\t\textra = defaultExtra || ( margin === true || value === true ? \"margin\" : \"border\" );\n\n\t\t\treturn access( this, function( elem, type, value ) {\n\t\t\t\tvar doc;\n\n\t\t\t\tif ( jQuery.isWindow( elem ) ) {\n\n\t\t\t\t\t// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there\n\t\t\t\t\t// isn't a whole lot we can do. See pull request at this URL for discussion:\n\t\t\t\t\t// https://github.com/jquery/jquery/pull/764\n\t\t\t\t\treturn elem.document.documentElement[ \"client\" + name ];\n\t\t\t\t}\n\n\t\t\t\t// Get document width or height\n\t\t\t\tif ( elem.nodeType === 9 ) {\n\t\t\t\t\tdoc = elem.documentElement;\n\n\t\t\t\t\t// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],\n\t\t\t\t\t// whichever is greatest\n\t\t\t\t\treturn Math.max(\n\t\t\t\t\t\telem.body[ \"scroll\" + name ], doc[ \"scroll\" + name ],\n\t\t\t\t\t\telem.body[ \"offset\" + name ], doc[ \"offset\" + name ],\n\t\t\t\t\t\tdoc[ \"client\" + name ]\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn value === undefined ?\n\n\t\t\t\t\t// Get width or height on the element, requesting but not forcing parseFloat\n\t\t\t\t\tjQuery.css( elem, type, extra ) :\n\n\t\t\t\t\t// Set width or height on the element\n\t\t\t\t\tjQuery.style( elem, type, value, extra );\n\t\t\t}, type, chainable ? margin : undefined, chainable, null );\n\t\t};\n\t} );\n} );\n\n\njQuery.fn.extend( {\n\n\tbind: function( types, data, fn ) {\n\t\treturn this.on( types, null, data, fn );\n\t},\n\tunbind: function( types, fn ) {\n\t\treturn this.off( types, null, fn );\n\t},\n\n\tdelegate: function( selector, types, data, fn ) {\n\t\treturn this.on( types, selector, data, fn );\n\t},\n\tundelegate: function( selector, types, fn ) {\n\n\t\t// ( namespace ) or ( selector, types [, fn] )\n\t\treturn arguments.length === 1 ?\n\t\t\tthis.off( selector, \"**\" ) :\n\t\t\tthis.off( types, selector || \"**\", fn );\n\t},\n\tsize: function() {\n\t\treturn this.length;\n\t}\n} );\n\njQuery.fn.andSelf = jQuery.fn.addBack;\n\n\n\n\n// Register as a named AMD module, since jQuery can be concatenated with other\n// files that may use define, but not via a proper concatenation script that\n// understands anonymous AMD modules. A named AMD is safest and most robust\n// way to register. Lowercase jquery is used because AMD module names are\n// derived from file names, and jQuery is normally delivered in a lowercase\n// file name. Do this after creating the global so that if an AMD module wants\n// to call noConflict to hide this version of jQuery, it will work.\n\n// Note that for maximum portability, libraries that are not jQuery should\n// declare themselves as anonymous modules, and avoid setting a global if an\n// AMD loader is present. jQuery is a special case. For more information, see\n// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon\n\nif ( typeof define === \"function\" && define.amd ) {\n\tdefine( \"jquery\", [], function() {\n\t\treturn jQuery;\n\t} );\n}\n\n\n\nvar\n\n\t// Map over jQuery in case of overwrite\n\t_jQuery = window.jQuery,\n\n\t// Map over the $ in case of overwrite\n\t_$ = window.$;\n\njQuery.noConflict = function( deep ) {\n\tif ( window.$ === jQuery ) {\n\t\twindow.$ = _$;\n\t}\n\n\tif ( deep && window.jQuery === jQuery ) {\n\t\twindow.jQuery = _jQuery;\n\t}\n\n\treturn jQuery;\n};\n\n// Expose jQuery and $ identifiers, even in AMD\n// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)\n// and CommonJS for browser emulators (#13566)\nif ( !noGlobal ) {\n\twindow.jQuery = window.$ = jQuery;\n}\n\nreturn jQuery;\n}));\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/jquery-validation/.bower.json",
    "content": "{\n  \"name\": \"jquery-validation\",\n  \"homepage\": \"http://jqueryvalidation.org/\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/jzaefferer/jquery-validation.git\"\n  },\n  \"authors\": [\n    \"Jörn Zaefferer <joern.zaefferer@gmail.com>\"\n  ],\n  \"description\": \"Form validation made easy\",\n  \"main\": \"dist/jquery.validate.js\",\n  \"keywords\": [\n    \"forms\",\n    \"validation\",\n    \"validate\"\n  ],\n  \"license\": \"MIT\",\n  \"ignore\": [\n    \"**/.*\",\n    \"node_modules\",\n    \"bower_components\",\n    \"test\",\n    \"demo\",\n    \"lib\"\n  ],\n  \"dependencies\": {\n    \"jquery\": \">= 1.7.2\"\n  },\n  \"version\": \"1.14.0\",\n  \"_release\": \"1.14.0\",\n  \"_resolution\": {\n    \"type\": \"version\",\n    \"tag\": \"1.14.0\",\n    \"commit\": \"c1343fb9823392aa9acbe1c3ffd337b8c92fed48\"\n  },\n  \"_source\": \"git://github.com/jzaefferer/jquery-validation.git\",\n  \"_target\": \">=1.8\",\n  \"_originalSource\": \"jquery-validation\"\n}"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/jquery-validation/LICENSE.md",
    "content": "The MIT License (MIT)\n=====================\n\nCopyright Jörn Zaefferer\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/jquery-validation/dist/additional-methods.js",
    "content": "/*!\n * jQuery Validation Plugin v1.14.0\n *\n * http://jqueryvalidation.org/\n *\n * Copyright (c) 2015 Jörn Zaefferer\n * Released under the MIT license\n */\n(function( factory ) {\n\tif ( typeof define === \"function\" && define.amd ) {\n\t\tdefine( [\"jquery\", \"./jquery.validate\"], factory );\n\t} else {\n\t\tfactory( jQuery );\n\t}\n}(function( $ ) {\n\n(function() {\n\n\tfunction stripHtml(value) {\n\t\t// remove html tags and space chars\n\t\treturn value.replace(/<.[^<>]*?>/g, \" \").replace(/&nbsp;|&#160;/gi, \" \")\n\t\t// remove punctuation\n\t\t.replace(/[.(),;:!?%#$'\\\"_+=\\/\\-“”’]*/g, \"\");\n\t}\n\n\t$.validator.addMethod(\"maxWords\", function(value, element, params) {\n\t\treturn this.optional(element) || stripHtml(value).match(/\\b\\w+\\b/g).length <= params;\n\t}, $.validator.format(\"Please enter {0} words or less.\"));\n\n\t$.validator.addMethod(\"minWords\", function(value, element, params) {\n\t\treturn this.optional(element) || stripHtml(value).match(/\\b\\w+\\b/g).length >= params;\n\t}, $.validator.format(\"Please enter at least {0} words.\"));\n\n\t$.validator.addMethod(\"rangeWords\", function(value, element, params) {\n\t\tvar valueStripped = stripHtml(value),\n\t\t\tregex = /\\b\\w+\\b/g;\n\t\treturn this.optional(element) || valueStripped.match(regex).length >= params[0] && valueStripped.match(regex).length <= params[1];\n\t}, $.validator.format(\"Please enter between {0} and {1} words.\"));\n\n}());\n\n// Accept a value from a file input based on a required mimetype\n$.validator.addMethod(\"accept\", function(value, element, param) {\n\t// Split mime on commas in case we have multiple types we can accept\n\tvar typeParam = typeof param === \"string\" ? param.replace(/\\s/g, \"\").replace(/,/g, \"|\") : \"image/*\",\n\toptionalValue = this.optional(element),\n\ti, file;\n\n\t// Element is optional\n\tif (optionalValue) {\n\t\treturn optionalValue;\n\t}\n\n\tif ($(element).attr(\"type\") === \"file\") {\n\t\t// If we are using a wildcard, make it regex friendly\n\t\ttypeParam = typeParam.replace(/\\*/g, \".*\");\n\n\t\t// Check if the element has a FileList before checking each file\n\t\tif (element.files && element.files.length) {\n\t\t\tfor (i = 0; i < element.files.length; i++) {\n\t\t\t\tfile = element.files[i];\n\n\t\t\t\t// Grab the mimetype from the loaded file, verify it matches\n\t\t\t\tif (!file.type.match(new RegExp( \"\\\\.?(\" + typeParam + \")$\", \"i\"))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Either return true because we've validated each file, or because the\n\t// browser does not support element.files and the FileList feature\n\treturn true;\n}, $.validator.format(\"Please enter a value with a valid mimetype.\"));\n\n$.validator.addMethod(\"alphanumeric\", function(value, element) {\n\treturn this.optional(element) || /^\\w+$/i.test(value);\n}, \"Letters, numbers, and underscores only please\");\n\n/*\n * Dutch bank account numbers (not 'giro' numbers) have 9 digits\n * and pass the '11 check'.\n * We accept the notation with spaces, as that is common.\n * acceptable: 123456789 or 12 34 56 789\n */\n$.validator.addMethod(\"bankaccountNL\", function(value, element) {\n\tif (this.optional(element)) {\n\t\treturn true;\n\t}\n\tif (!(/^[0-9]{9}|([0-9]{2} ){3}[0-9]{3}$/.test(value))) {\n\t\treturn false;\n\t}\n\t// now '11 check'\n\tvar account = value.replace(/ /g, \"\"), // remove spaces\n\t\tsum = 0,\n\t\tlen = account.length,\n\t\tpos, factor, digit;\n\tfor ( pos = 0; pos < len; pos++ ) {\n\t\tfactor = len - pos;\n\t\tdigit = account.substring(pos, pos + 1);\n\t\tsum = sum + factor * digit;\n\t}\n\treturn sum % 11 === 0;\n}, \"Please specify a valid bank account number\");\n\n$.validator.addMethod(\"bankorgiroaccountNL\", function(value, element) {\n\treturn this.optional(element) ||\n\t\t\t($.validator.methods.bankaccountNL.call(this, value, element)) ||\n\t\t\t($.validator.methods.giroaccountNL.call(this, value, element));\n}, \"Please specify a valid bank or giro account number\");\n\n/**\n * BIC is the business identifier code (ISO 9362). This BIC check is not a guarantee for authenticity.\n *\n * BIC pattern: BBBBCCLLbbb (8 or 11 characters long; bbb is optional)\n *\n * BIC definition in detail:\n * - First 4 characters - bank code (only letters)\n * - Next 2 characters - ISO 3166-1 alpha-2 country code (only letters)\n * - Next 2 characters - location code (letters and digits)\n *   a. shall not start with '0' or '1'\n *   b. second character must be a letter ('O' is not allowed) or one of the following digits ('0' for test (therefore not allowed), '1' for passive participant and '2' for active participant)\n * - Last 3 characters - branch code, optional (shall not start with 'X' except in case of 'XXX' for primary office) (letters and digits)\n */\n$.validator.addMethod(\"bic\", function(value, element) {\n    return this.optional( element ) || /^([A-Z]{6}[A-Z2-9][A-NP-Z1-2])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/.test( value );\n}, \"Please specify a valid BIC code\");\n\n/*\n * Código de identificación fiscal ( CIF ) is the tax identification code for Spanish legal entities\n * Further rules can be found in Spanish on http://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal\n */\n$.validator.addMethod( \"cifES\", function( value ) {\n\t\"use strict\";\n\n\tvar num = [],\n\t\tcontrolDigit, sum, i, count, tmp, secondDigit;\n\n\tvalue = value.toUpperCase();\n\n\t// Quick format test\n\tif ( !value.match( \"((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)\" ) ) {\n\t\treturn false;\n\t}\n\n\tfor ( i = 0; i < 9; i++ ) {\n\t\tnum[ i ] = parseInt( value.charAt( i ), 10 );\n\t}\n\n\t// Algorithm for checking CIF codes\n\tsum = num[ 2 ] + num[ 4 ] + num[ 6 ];\n\tfor ( count = 1; count < 8; count += 2 ) {\n\t\ttmp = ( 2 * num[ count ] ).toString();\n\t\tsecondDigit = tmp.charAt( 1 );\n\n\t\tsum += parseInt( tmp.charAt( 0 ), 10 ) + ( secondDigit === \"\" ? 0 : parseInt( secondDigit, 10 ) );\n\t}\n\n\t/* The first (position 1) is a letter following the following criteria:\n\t *\tA. Corporations\n\t *\tB. LLCs\n\t *\tC. General partnerships\n\t *\tD. Companies limited partnerships\n\t *\tE. Communities of goods\n\t *\tF. Cooperative Societies\n\t *\tG. Associations\n\t *\tH. Communities of homeowners in horizontal property regime\n\t *\tJ. Civil Societies\n\t *\tK. Old format\n\t *\tL. Old format\n\t *\tM. Old format\n\t *\tN. Nonresident entities\n\t *\tP. Local authorities\n\t *\tQ. Autonomous bodies, state or not, and the like, and congregations and religious institutions\n\t *\tR. Congregations and religious institutions (since 2008 ORDER EHA/451/2008)\n\t *\tS. Organs of State Administration and regions\n\t *\tV. Agrarian Transformation\n\t *\tW. Permanent establishments of non-resident in Spain\n\t */\n\tif ( /^[ABCDEFGHJNPQRSUVW]{1}/.test( value ) ) {\n\t\tsum += \"\";\n\t\tcontrolDigit = 10 - parseInt( sum.charAt( sum.length - 1 ), 10 );\n\t\tvalue += controlDigit;\n\t\treturn ( num[ 8 ].toString() === String.fromCharCode( 64 + controlDigit ) || num[ 8 ].toString() === value.charAt( value.length - 1 ) );\n\t}\n\n\treturn false;\n\n}, \"Please specify a valid CIF number.\" );\n\n/*\n * Brazillian CPF number (Cadastrado de Pessoas Físicas) is the equivalent of a Brazilian tax registration number.\n * CPF numbers have 11 digits in total: 9 numbers followed by 2 check numbers that are being used for validation.\n */\n$.validator.addMethod(\"cpfBR\", function(value) {\n\t// Removing special characters from value\n\tvalue = value.replace(/([~!@#$%^&*()_+=`{}\\[\\]\\-|\\\\:;'<>,.\\/? ])+/g, \"\");\n\n\t// Checking value to have 11 digits only\n\tif (value.length !== 11) {\n\t\treturn false;\n\t}\n\n\tvar sum = 0,\n\t\tfirstCN, secondCN, checkResult, i;\n\n\tfirstCN = parseInt(value.substring(9, 10), 10);\n\tsecondCN = parseInt(value.substring(10, 11), 10);\n\n\tcheckResult = function(sum, cn) {\n\t\tvar result = (sum * 10) % 11;\n\t\tif ((result === 10) || (result === 11)) {result = 0;}\n\t\treturn (result === cn);\n\t};\n\n\t// Checking for dump data\n\tif (value === \"\" ||\n\t\tvalue === \"00000000000\" ||\n\t\tvalue === \"11111111111\" ||\n\t\tvalue === \"22222222222\" ||\n\t\tvalue === \"33333333333\" ||\n\t\tvalue === \"44444444444\" ||\n\t\tvalue === \"55555555555\" ||\n\t\tvalue === \"66666666666\" ||\n\t\tvalue === \"77777777777\" ||\n\t\tvalue === \"88888888888\" ||\n\t\tvalue === \"99999999999\"\n\t) {\n\t\treturn false;\n\t}\n\n\t// Step 1 - using first Check Number:\n\tfor ( i = 1; i <= 9; i++ ) {\n\t\tsum = sum + parseInt(value.substring(i - 1, i), 10) * (11 - i);\n\t}\n\n\t// If first Check Number (CN) is valid, move to Step 2 - using second Check Number:\n\tif ( checkResult(sum, firstCN) ) {\n\t\tsum = 0;\n\t\tfor ( i = 1; i <= 10; i++ ) {\n\t\t\tsum = sum + parseInt(value.substring(i - 1, i), 10) * (12 - i);\n\t\t}\n\t\treturn checkResult(sum, secondCN);\n\t}\n\treturn false;\n\n}, \"Please specify a valid CPF number\");\n\n/* NOTICE: Modified version of Castle.Components.Validator.CreditCardValidator\n * Redistributed under the the Apache License 2.0 at http://www.apache.org/licenses/LICENSE-2.0\n * Valid Types: mastercard, visa, amex, dinersclub, enroute, discover, jcb, unknown, all (overrides all other settings)\n */\n$.validator.addMethod(\"creditcardtypes\", function(value, element, param) {\n\tif (/[^0-9\\-]+/.test(value)) {\n\t\treturn false;\n\t}\n\n\tvalue = value.replace(/\\D/g, \"\");\n\n\tvar validTypes = 0x0000;\n\n\tif (param.mastercard) {\n\t\tvalidTypes |= 0x0001;\n\t}\n\tif (param.visa) {\n\t\tvalidTypes |= 0x0002;\n\t}\n\tif (param.amex) {\n\t\tvalidTypes |= 0x0004;\n\t}\n\tif (param.dinersclub) {\n\t\tvalidTypes |= 0x0008;\n\t}\n\tif (param.enroute) {\n\t\tvalidTypes |= 0x0010;\n\t}\n\tif (param.discover) {\n\t\tvalidTypes |= 0x0020;\n\t}\n\tif (param.jcb) {\n\t\tvalidTypes |= 0x0040;\n\t}\n\tif (param.unknown) {\n\t\tvalidTypes |= 0x0080;\n\t}\n\tif (param.all) {\n\t\tvalidTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080;\n\t}\n\tif (validTypes & 0x0001 && /^(5[12345])/.test(value)) { //mastercard\n\t\treturn value.length === 16;\n\t}\n\tif (validTypes & 0x0002 && /^(4)/.test(value)) { //visa\n\t\treturn value.length === 16;\n\t}\n\tif (validTypes & 0x0004 && /^(3[47])/.test(value)) { //amex\n\t\treturn value.length === 15;\n\t}\n\tif (validTypes & 0x0008 && /^(3(0[012345]|[68]))/.test(value)) { //dinersclub\n\t\treturn value.length === 14;\n\t}\n\tif (validTypes & 0x0010 && /^(2(014|149))/.test(value)) { //enroute\n\t\treturn value.length === 15;\n\t}\n\tif (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover\n\t\treturn value.length === 16;\n\t}\n\tif (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb\n\t\treturn value.length === 16;\n\t}\n\tif (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb\n\t\treturn value.length === 15;\n\t}\n\tif (validTypes & 0x0080) { //unknown\n\t\treturn true;\n\t}\n\treturn false;\n}, \"Please enter a valid credit card number.\");\n\n/**\n * Validates currencies with any given symbols by @jameslouiz\n * Symbols can be optional or required. Symbols required by default\n *\n * Usage examples:\n *  currency: [\"£\", false] - Use false for soft currency validation\n *  currency: [\"$\", false]\n *  currency: [\"RM\", false] - also works with text based symbols such as \"RM\" - Malaysia Ringgit etc\n *\n *  <input class=\"currencyInput\" name=\"currencyInput\">\n *\n * Soft symbol checking\n *  currencyInput: {\n *     currency: [\"$\", false]\n *  }\n *\n * Strict symbol checking (default)\n *  currencyInput: {\n *     currency: \"$\"\n *     //OR\n *     currency: [\"$\", true]\n *  }\n *\n * Multiple Symbols\n *  currencyInput: {\n *     currency: \"$,£,¢\"\n *  }\n */\n$.validator.addMethod(\"currency\", function(value, element, param) {\n    var isParamString = typeof param === \"string\",\n        symbol = isParamString ? param : param[0],\n        soft = isParamString ? true : param[1],\n        regex;\n\n    symbol = symbol.replace(/,/g, \"\");\n    symbol = soft ? symbol + \"]\" : symbol + \"]?\";\n    regex = \"^[\" + symbol + \"([1-9]{1}[0-9]{0,2}(\\\\,[0-9]{3})*(\\\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\\\.[0-9]{0,2})?|0(\\\\.[0-9]{0,2})?|(\\\\.[0-9]{1,2})?)$\";\n    regex = new RegExp(regex);\n    return this.optional(element) || regex.test(value);\n\n}, \"Please specify a valid currency\");\n\n$.validator.addMethod(\"dateFA\", function(value, element) {\n\treturn this.optional(element) || /^[1-4]\\d{3}\\/((0?[1-6]\\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\\/(30|([1-2][0-9])|(0?[1-9]))))$/.test(value);\n}, $.validator.messages.date);\n\n/**\n * Return true, if the value is a valid date, also making this formal check dd/mm/yyyy.\n *\n * @example $.validator.methods.date(\"01/01/1900\")\n * @result true\n *\n * @example $.validator.methods.date(\"01/13/1990\")\n * @result false\n *\n * @example $.validator.methods.date(\"01.01.1900\")\n * @result false\n *\n * @example <input name=\"pippo\" class=\"{dateITA:true}\" />\n * @desc Declares an optional input element whose value must be a valid date.\n *\n * @name $.validator.methods.dateITA\n * @type Boolean\n * @cat Plugins/Validate/Methods\n */\n$.validator.addMethod(\"dateITA\", function(value, element) {\n\tvar check = false,\n\t\tre = /^\\d{1,2}\\/\\d{1,2}\\/\\d{4}$/,\n\t\tadata, gg, mm, aaaa, xdata;\n\tif ( re.test(value)) {\n\t\tadata = value.split(\"/\");\n\t\tgg = parseInt(adata[0], 10);\n\t\tmm = parseInt(adata[1], 10);\n\t\taaaa = parseInt(adata[2], 10);\n\t\txdata = new Date(Date.UTC(aaaa, mm - 1, gg, 12, 0, 0, 0));\n\t\tif ( ( xdata.getUTCFullYear() === aaaa ) && ( xdata.getUTCMonth () === mm - 1 ) && ( xdata.getUTCDate() === gg ) ) {\n\t\t\tcheck = true;\n\t\t} else {\n\t\t\tcheck = false;\n\t\t}\n\t} else {\n\t\tcheck = false;\n\t}\n\treturn this.optional(element) || check;\n}, $.validator.messages.date);\n\n$.validator.addMethod(\"dateNL\", function(value, element) {\n\treturn this.optional(element) || /^(0?[1-9]|[12]\\d|3[01])[\\.\\/\\-](0?[1-9]|1[012])[\\.\\/\\-]([12]\\d)?(\\d\\d)$/.test(value);\n}, $.validator.messages.date);\n\n// Older \"accept\" file extension method. Old docs: http://docs.jquery.com/Plugins/Validation/Methods/accept\n$.validator.addMethod(\"extension\", function(value, element, param) {\n\tparam = typeof param === \"string\" ? param.replace(/,/g, \"|\") : \"png|jpe?g|gif\";\n\treturn this.optional(element) || value.match(new RegExp(\"\\\\.(\" + param + \")$\", \"i\"));\n}, $.validator.format(\"Please enter a value with a valid extension.\"));\n\n/**\n * Dutch giro account numbers (not bank numbers) have max 7 digits\n */\n$.validator.addMethod(\"giroaccountNL\", function(value, element) {\n\treturn this.optional(element) || /^[0-9]{1,7}$/.test(value);\n}, \"Please specify a valid giro account number\");\n\n/**\n * IBAN is the international bank account number.\n * It has a country - specific format, that is checked here too\n */\n$.validator.addMethod(\"iban\", function(value, element) {\n\t// some quick simple tests to prevent needless work\n\tif (this.optional(element)) {\n\t\treturn true;\n\t}\n\n\t// remove spaces and to upper case\n\tvar iban = value.replace(/ /g, \"\").toUpperCase(),\n\t\tibancheckdigits = \"\",\n\t\tleadingZeroes = true,\n\t\tcRest = \"\",\n\t\tcOperator = \"\",\n\t\tcountrycode, ibancheck, charAt, cChar, bbanpattern, bbancountrypatterns, ibanregexp, i, p;\n\n\t// check the country code and find the country specific format\n\tcountrycode = iban.substring(0, 2);\n\tbbancountrypatterns = {\n\t\t\"AL\": \"\\\\d{8}[\\\\dA-Z]{16}\",\n\t\t\"AD\": \"\\\\d{8}[\\\\dA-Z]{12}\",\n\t\t\"AT\": \"\\\\d{16}\",\n\t\t\"AZ\": \"[\\\\dA-Z]{4}\\\\d{20}\",\n\t\t\"BE\": \"\\\\d{12}\",\n\t\t\"BH\": \"[A-Z]{4}[\\\\dA-Z]{14}\",\n\t\t\"BA\": \"\\\\d{16}\",\n\t\t\"BR\": \"\\\\d{23}[A-Z][\\\\dA-Z]\",\n\t\t\"BG\": \"[A-Z]{4}\\\\d{6}[\\\\dA-Z]{8}\",\n\t\t\"CR\": \"\\\\d{17}\",\n\t\t\"HR\": \"\\\\d{17}\",\n\t\t\"CY\": \"\\\\d{8}[\\\\dA-Z]{16}\",\n\t\t\"CZ\": \"\\\\d{20}\",\n\t\t\"DK\": \"\\\\d{14}\",\n\t\t\"DO\": \"[A-Z]{4}\\\\d{20}\",\n\t\t\"EE\": \"\\\\d{16}\",\n\t\t\"FO\": \"\\\\d{14}\",\n\t\t\"FI\": \"\\\\d{14}\",\n\t\t\"FR\": \"\\\\d{10}[\\\\dA-Z]{11}\\\\d{2}\",\n\t\t\"GE\": \"[\\\\dA-Z]{2}\\\\d{16}\",\n\t\t\"DE\": \"\\\\d{18}\",\n\t\t\"GI\": \"[A-Z]{4}[\\\\dA-Z]{15}\",\n\t\t\"GR\": \"\\\\d{7}[\\\\dA-Z]{16}\",\n\t\t\"GL\": \"\\\\d{14}\",\n\t\t\"GT\": \"[\\\\dA-Z]{4}[\\\\dA-Z]{20}\",\n\t\t\"HU\": \"\\\\d{24}\",\n\t\t\"IS\": \"\\\\d{22}\",\n\t\t\"IE\": \"[\\\\dA-Z]{4}\\\\d{14}\",\n\t\t\"IL\": \"\\\\d{19}\",\n\t\t\"IT\": \"[A-Z]\\\\d{10}[\\\\dA-Z]{12}\",\n\t\t\"KZ\": \"\\\\d{3}[\\\\dA-Z]{13}\",\n\t\t\"KW\": \"[A-Z]{4}[\\\\dA-Z]{22}\",\n\t\t\"LV\": \"[A-Z]{4}[\\\\dA-Z]{13}\",\n\t\t\"LB\": \"\\\\d{4}[\\\\dA-Z]{20}\",\n\t\t\"LI\": \"\\\\d{5}[\\\\dA-Z]{12}\",\n\t\t\"LT\": \"\\\\d{16}\",\n\t\t\"LU\": \"\\\\d{3}[\\\\dA-Z]{13}\",\n\t\t\"MK\": \"\\\\d{3}[\\\\dA-Z]{10}\\\\d{2}\",\n\t\t\"MT\": \"[A-Z]{4}\\\\d{5}[\\\\dA-Z]{18}\",\n\t\t\"MR\": \"\\\\d{23}\",\n\t\t\"MU\": \"[A-Z]{4}\\\\d{19}[A-Z]{3}\",\n\t\t\"MC\": \"\\\\d{10}[\\\\dA-Z]{11}\\\\d{2}\",\n\t\t\"MD\": \"[\\\\dA-Z]{2}\\\\d{18}\",\n\t\t\"ME\": \"\\\\d{18}\",\n\t\t\"NL\": \"[A-Z]{4}\\\\d{10}\",\n\t\t\"NO\": \"\\\\d{11}\",\n\t\t\"PK\": \"[\\\\dA-Z]{4}\\\\d{16}\",\n\t\t\"PS\": \"[\\\\dA-Z]{4}\\\\d{21}\",\n\t\t\"PL\": \"\\\\d{24}\",\n\t\t\"PT\": \"\\\\d{21}\",\n\t\t\"RO\": \"[A-Z]{4}[\\\\dA-Z]{16}\",\n\t\t\"SM\": \"[A-Z]\\\\d{10}[\\\\dA-Z]{12}\",\n\t\t\"SA\": \"\\\\d{2}[\\\\dA-Z]{18}\",\n\t\t\"RS\": \"\\\\d{18}\",\n\t\t\"SK\": \"\\\\d{20}\",\n\t\t\"SI\": \"\\\\d{15}\",\n\t\t\"ES\": \"\\\\d{20}\",\n\t\t\"SE\": \"\\\\d{20}\",\n\t\t\"CH\": \"\\\\d{5}[\\\\dA-Z]{12}\",\n\t\t\"TN\": \"\\\\d{20}\",\n\t\t\"TR\": \"\\\\d{5}[\\\\dA-Z]{17}\",\n\t\t\"AE\": \"\\\\d{3}\\\\d{16}\",\n\t\t\"GB\": \"[A-Z]{4}\\\\d{14}\",\n\t\t\"VG\": \"[\\\\dA-Z]{4}\\\\d{16}\"\n\t};\n\n\tbbanpattern = bbancountrypatterns[countrycode];\n\t// As new countries will start using IBAN in the\n\t// future, we only check if the countrycode is known.\n\t// This prevents false negatives, while almost all\n\t// false positives introduced by this, will be caught\n\t// by the checksum validation below anyway.\n\t// Strict checking should return FALSE for unknown\n\t// countries.\n\tif (typeof bbanpattern !== \"undefined\") {\n\t\tibanregexp = new RegExp(\"^[A-Z]{2}\\\\d{2}\" + bbanpattern + \"$\", \"\");\n\t\tif (!(ibanregexp.test(iban))) {\n\t\t\treturn false; // invalid country specific format\n\t\t}\n\t}\n\n\t// now check the checksum, first convert to digits\n\tibancheck = iban.substring(4, iban.length) + iban.substring(0, 4);\n\tfor (i = 0; i < ibancheck.length; i++) {\n\t\tcharAt = ibancheck.charAt(i);\n\t\tif (charAt !== \"0\") {\n\t\t\tleadingZeroes = false;\n\t\t}\n\t\tif (!leadingZeroes) {\n\t\t\tibancheckdigits += \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\".indexOf(charAt);\n\t\t}\n\t}\n\n\t// calculate the result of: ibancheckdigits % 97\n\tfor (p = 0; p < ibancheckdigits.length; p++) {\n\t\tcChar = ibancheckdigits.charAt(p);\n\t\tcOperator = \"\" + cRest + \"\" + cChar;\n\t\tcRest = cOperator % 97;\n\t}\n\treturn cRest === 1;\n}, \"Please specify a valid IBAN\");\n\n$.validator.addMethod(\"integer\", function(value, element) {\n\treturn this.optional(element) || /^-?\\d+$/.test(value);\n}, \"A positive or negative non-decimal number please\");\n\n$.validator.addMethod(\"ipv4\", function(value, element) {\n\treturn this.optional(element) || /^(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$/i.test(value);\n}, \"Please enter a valid IP v4 address.\");\n\n$.validator.addMethod(\"ipv6\", function(value, element) {\n\treturn this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b)\\.){3}(\\b((25[0-5])|(1\\d{2})|(2[0-4]\\d)|(\\d{1,2}))\\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);\n}, \"Please enter a valid IP v6 address.\");\n\n$.validator.addMethod(\"lettersonly\", function(value, element) {\n\treturn this.optional(element) || /^[a-z]+$/i.test(value);\n}, \"Letters only please\");\n\n$.validator.addMethod(\"letterswithbasicpunc\", function(value, element) {\n\treturn this.optional(element) || /^[a-z\\-.,()'\"\\s]+$/i.test(value);\n}, \"Letters or punctuation only please\");\n\n$.validator.addMethod(\"mobileNL\", function(value, element) {\n\treturn this.optional(element) || /^((\\+|00(\\s|\\s?\\-\\s?)?)31(\\s|\\s?\\-\\s?)?(\\(0\\)[\\-\\s]?)?|0)6((\\s|\\s?\\-\\s?)?[0-9]){8}$/.test(value);\n}, \"Please specify a valid mobile number\");\n\n/* For UK phone functions, do the following server side processing:\n * Compare original input with this RegEx pattern:\n * ^\\(?(?:(?:00\\)?[\\s\\-]?\\(?|\\+)(44)\\)?[\\s\\-]?\\(?(?:0\\)?[\\s\\-]?\\(?)?|0)([1-9]\\d{1,4}\\)?[\\s\\d\\-]+)$\n * Extract $1 and set $prefix to '+44<space>' if $1 is '44', otherwise set $prefix to '0'\n * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2.\n * A number of very detailed GB telephone number RegEx patterns can also be found at:\n * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers\n */\n$.validator.addMethod(\"mobileUK\", function(phone_number, element) {\n\tphone_number = phone_number.replace(/\\(|\\)|\\s+|-/g, \"\");\n\treturn this.optional(element) || phone_number.length > 9 &&\n\t\tphone_number.match(/^(?:(?:(?:00\\s?|\\+)44\\s?|0)7(?:[1345789]\\d{2}|624)\\s?\\d{3}\\s?\\d{3})$/);\n}, \"Please specify a valid mobile number\");\n\n/*\n * The número de identidad de extranjero ( NIE )is a code used to identify the non-nationals in Spain\n */\n$.validator.addMethod( \"nieES\", function( value ) {\n\t\"use strict\";\n\n\tvalue = value.toUpperCase();\n\n\t// Basic format test\n\tif ( !value.match( \"((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)\" ) ) {\n\t\treturn false;\n\t}\n\n\t// Test NIE\n\t//T\n\tif ( /^[T]{1}/.test( value ) ) {\n\t\treturn ( value[ 8 ] === /^[T]{1}[A-Z0-9]{8}$/.test( value ) );\n\t}\n\n\t//XYZ\n\tif ( /^[XYZ]{1}/.test( value ) ) {\n\t\treturn (\n\t\t\tvalue[ 8 ] === \"TRWAGMYFPDXBNJZSQVHLCKE\".charAt(\n\t\t\t\tvalue.replace( \"X\", \"0\" )\n\t\t\t\t\t.replace( \"Y\", \"1\" )\n\t\t\t\t\t.replace( \"Z\", \"2\" )\n\t\t\t\t\t.substring( 0, 8 ) % 23\n\t\t\t)\n\t\t);\n\t}\n\n\treturn false;\n\n}, \"Please specify a valid NIE number.\" );\n\n/*\n * The Número de Identificación Fiscal ( NIF ) is the way tax identification used in Spain for individuals\n */\n$.validator.addMethod( \"nifES\", function( value ) {\n\t\"use strict\";\n\n\tvalue = value.toUpperCase();\n\n\t// Basic format test\n\tif ( !value.match(\"((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)\") ) {\n\t\treturn false;\n\t}\n\n\t// Test NIF\n\tif ( /^[0-9]{8}[A-Z]{1}$/.test( value ) ) {\n\t\treturn ( \"TRWAGMYFPDXBNJZSQVHLCKE\".charAt( value.substring( 8, 0 ) % 23 ) === value.charAt( 8 ) );\n\t}\n\t// Test specials NIF (starts with K, L or M)\n\tif ( /^[KLM]{1}/.test( value ) ) {\n\t\treturn ( value[ 8 ] === String.fromCharCode( 64 ) );\n\t}\n\n\treturn false;\n\n}, \"Please specify a valid NIF number.\" );\n\njQuery.validator.addMethod( \"notEqualTo\", function( value, element, param ) {\n\treturn this.optional(element) || !$.validator.methods.equalTo.call( this, value, element, param );\n}, \"Please enter a different value, values must not be the same.\" );\n\n$.validator.addMethod(\"nowhitespace\", function(value, element) {\n\treturn this.optional(element) || /^\\S+$/i.test(value);\n}, \"No white space please\");\n\n/**\n* Return true if the field value matches the given format RegExp\n*\n* @example $.validator.methods.pattern(\"AR1004\",element,/^AR\\d{4}$/)\n* @result true\n*\n* @example $.validator.methods.pattern(\"BR1004\",element,/^AR\\d{4}$/)\n* @result false\n*\n* @name $.validator.methods.pattern\n* @type Boolean\n* @cat Plugins/Validate/Methods\n*/\n$.validator.addMethod(\"pattern\", function(value, element, param) {\n\tif (this.optional(element)) {\n\t\treturn true;\n\t}\n\tif (typeof param === \"string\") {\n\t\tparam = new RegExp(\"^(?:\" + param + \")$\");\n\t}\n\treturn param.test(value);\n}, \"Invalid format.\");\n\n/**\n * Dutch phone numbers have 10 digits (or 11 and start with +31).\n */\n$.validator.addMethod(\"phoneNL\", function(value, element) {\n\treturn this.optional(element) || /^((\\+|00(\\s|\\s?\\-\\s?)?)31(\\s|\\s?\\-\\s?)?(\\(0\\)[\\-\\s]?)?|0)[1-9]((\\s|\\s?\\-\\s?)?[0-9]){8}$/.test(value);\n}, \"Please specify a valid phone number.\");\n\n/* For UK phone functions, do the following server side processing:\n * Compare original input with this RegEx pattern:\n * ^\\(?(?:(?:00\\)?[\\s\\-]?\\(?|\\+)(44)\\)?[\\s\\-]?\\(?(?:0\\)?[\\s\\-]?\\(?)?|0)([1-9]\\d{1,4}\\)?[\\s\\d\\-]+)$\n * Extract $1 and set $prefix to '+44<space>' if $1 is '44', otherwise set $prefix to '0'\n * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2.\n * A number of very detailed GB telephone number RegEx patterns can also be found at:\n * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers\n */\n$.validator.addMethod(\"phoneUK\", function(phone_number, element) {\n\tphone_number = phone_number.replace(/\\(|\\)|\\s+|-/g, \"\");\n\treturn this.optional(element) || phone_number.length > 9 &&\n\t\tphone_number.match(/^(?:(?:(?:00\\s?|\\+)44\\s?)|(?:\\(?0))(?:\\d{2}\\)?\\s?\\d{4}\\s?\\d{4}|\\d{3}\\)?\\s?\\d{3}\\s?\\d{3,4}|\\d{4}\\)?\\s?(?:\\d{5}|\\d{3}\\s?\\d{3})|\\d{5}\\)?\\s?\\d{4,5})$/);\n}, \"Please specify a valid phone number\");\n\n/**\n * matches US phone number format\n *\n * where the area code may not start with 1 and the prefix may not start with 1\n * allows '-' or ' ' as a separator and allows parens around area code\n * some people may want to put a '1' in front of their number\n *\n * 1(212)-999-2345 or\n * 212 999 2344 or\n * 212-999-0983\n *\n * but not\n * 111-123-5434\n * and not\n * 212 123 4567\n */\n$.validator.addMethod(\"phoneUS\", function(phone_number, element) {\n\tphone_number = phone_number.replace(/\\s+/g, \"\");\n\treturn this.optional(element) || phone_number.length > 9 &&\n\t\tphone_number.match(/^(\\+?1-?)?(\\([2-9]([02-9]\\d|1[02-9])\\)|[2-9]([02-9]\\d|1[02-9]))-?[2-9]([02-9]\\d|1[02-9])-?\\d{4}$/);\n}, \"Please specify a valid phone number\");\n\n/* For UK phone functions, do the following server side processing:\n * Compare original input with this RegEx pattern:\n * ^\\(?(?:(?:00\\)?[\\s\\-]?\\(?|\\+)(44)\\)?[\\s\\-]?\\(?(?:0\\)?[\\s\\-]?\\(?)?|0)([1-9]\\d{1,4}\\)?[\\s\\d\\-]+)$\n * Extract $1 and set $prefix to '+44<space>' if $1 is '44', otherwise set $prefix to '0'\n * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2.\n * A number of very detailed GB telephone number RegEx patterns can also be found at:\n * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers\n */\n//Matches UK landline + mobile, accepting only 01-3 for landline or 07 for mobile to exclude many premium numbers\n$.validator.addMethod(\"phonesUK\", function(phone_number, element) {\n\tphone_number = phone_number.replace(/\\(|\\)|\\s+|-/g, \"\");\n\treturn this.optional(element) || phone_number.length > 9 &&\n\t\tphone_number.match(/^(?:(?:(?:00\\s?|\\+)44\\s?|0)(?:1\\d{8,9}|[23]\\d{9}|7(?:[1345789]\\d{8}|624\\d{6})))$/);\n}, \"Please specify a valid uk phone number\");\n\n/**\n * Matches a valid Canadian Postal Code\n *\n * @example jQuery.validator.methods.postalCodeCA( \"H0H 0H0\", element )\n * @result true\n *\n * @example jQuery.validator.methods.postalCodeCA( \"H0H0H0\", element )\n * @result false\n *\n * @name jQuery.validator.methods.postalCodeCA\n * @type Boolean\n * @cat Plugins/Validate/Methods\n */\n$.validator.addMethod( \"postalCodeCA\", function( value, element ) {\n\treturn this.optional( element ) || /^[ABCEGHJKLMNPRSTVXY]\\d[A-Z] \\d[A-Z]\\d$/.test( value );\n}, \"Please specify a valid postal code\" );\n\n/*\n* Valida CEPs do brasileiros:\n*\n* Formatos aceitos:\n* 99999-999\n* 99.999-999\n* 99999999\n*/\n$.validator.addMethod(\"postalcodeBR\", function(cep_value, element) {\n\treturn this.optional(element) || /^\\d{2}.\\d{3}-\\d{3}?$|^\\d{5}-?\\d{3}?$/.test( cep_value );\n}, \"Informe um CEP válido.\");\n\n/* Matches Italian postcode (CAP) */\n$.validator.addMethod(\"postalcodeIT\", function(value, element) {\n\treturn this.optional(element) || /^\\d{5}$/.test(value);\n}, \"Please specify a valid postal code\");\n\n$.validator.addMethod(\"postalcodeNL\", function(value, element) {\n\treturn this.optional(element) || /^[1-9][0-9]{3}\\s?[a-zA-Z]{2}$/.test(value);\n}, \"Please specify a valid postal code\");\n\n// Matches UK postcode. Does not match to UK Channel Islands that have their own postcodes (non standard UK)\n$.validator.addMethod(\"postcodeUK\", function(value, element) {\n\treturn this.optional(element) || /^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\\s?(0AA))$/i.test(value);\n}, \"Please specify a valid UK postcode\");\n\n/*\n * Lets you say \"at least X inputs that match selector Y must be filled.\"\n *\n * The end result is that neither of these inputs:\n *\n *\t<input class=\"productinfo\" name=\"partnumber\">\n *\t<input class=\"productinfo\" name=\"description\">\n *\n *\t...will validate unless at least one of them is filled.\n *\n * partnumber:\t{require_from_group: [1,\".productinfo\"]},\n * description: {require_from_group: [1,\".productinfo\"]}\n *\n * options[0]: number of fields that must be filled in the group\n * options[1]: CSS selector that defines the group of conditionally required fields\n */\n$.validator.addMethod(\"require_from_group\", function(value, element, options) {\n\tvar $fields = $(options[1], element.form),\n\t\t$fieldsFirst = $fields.eq(0),\n\t\tvalidator = $fieldsFirst.data(\"valid_req_grp\") ? $fieldsFirst.data(\"valid_req_grp\") : $.extend({}, this),\n\t\tisValid = $fields.filter(function() {\n\t\t\treturn validator.elementValue(this);\n\t\t}).length >= options[0];\n\n\t// Store the cloned validator for future validation\n\t$fieldsFirst.data(\"valid_req_grp\", validator);\n\n\t// If element isn't being validated, run each require_from_group field's validation rules\n\tif (!$(element).data(\"being_validated\")) {\n\t\t$fields.data(\"being_validated\", true);\n\t\t$fields.each(function() {\n\t\t\tvalidator.element(this);\n\t\t});\n\t\t$fields.data(\"being_validated\", false);\n\t}\n\treturn isValid;\n}, $.validator.format(\"Please fill at least {0} of these fields.\"));\n\n/*\n * Lets you say \"either at least X inputs that match selector Y must be filled,\n * OR they must all be skipped (left blank).\"\n *\n * The end result, is that none of these inputs:\n *\n *\t<input class=\"productinfo\" name=\"partnumber\">\n *\t<input class=\"productinfo\" name=\"description\">\n *\t<input class=\"productinfo\" name=\"color\">\n *\n *\t...will validate unless either at least two of them are filled,\n *\tOR none of them are.\n *\n * partnumber:\t{skip_or_fill_minimum: [2,\".productinfo\"]},\n * description: {skip_or_fill_minimum: [2,\".productinfo\"]},\n * color:\t\t{skip_or_fill_minimum: [2,\".productinfo\"]}\n *\n * options[0]: number of fields that must be filled in the group\n * options[1]: CSS selector that defines the group of conditionally required fields\n *\n */\n$.validator.addMethod(\"skip_or_fill_minimum\", function(value, element, options) {\n\tvar $fields = $(options[1], element.form),\n\t\t$fieldsFirst = $fields.eq(0),\n\t\tvalidator = $fieldsFirst.data(\"valid_skip\") ? $fieldsFirst.data(\"valid_skip\") : $.extend({}, this),\n\t\tnumberFilled = $fields.filter(function() {\n\t\t\treturn validator.elementValue(this);\n\t\t}).length,\n\t\tisValid = numberFilled === 0 || numberFilled >= options[0];\n\n\t// Store the cloned validator for future validation\n\t$fieldsFirst.data(\"valid_skip\", validator);\n\n\t// If element isn't being validated, run each skip_or_fill_minimum field's validation rules\n\tif (!$(element).data(\"being_validated\")) {\n\t\t$fields.data(\"being_validated\", true);\n\t\t$fields.each(function() {\n\t\t\tvalidator.element(this);\n\t\t});\n\t\t$fields.data(\"being_validated\", false);\n\t}\n\treturn isValid;\n}, $.validator.format(\"Please either skip these fields or fill at least {0} of them.\"));\n\n/* Validates US States and/or Territories by @jdforsythe\n * Can be case insensitive or require capitalization - default is case insensitive\n * Can include US Territories or not - default does not\n * Can include US Military postal abbreviations (AA, AE, AP) - default does not\n *\n * Note: \"States\" always includes DC (District of Colombia)\n *\n * Usage examples:\n *\n *  This is the default - case insensitive, no territories, no military zones\n *  stateInput: {\n *     caseSensitive: false,\n *     includeTerritories: false,\n *     includeMilitary: false\n *  }\n *\n *  Only allow capital letters, no territories, no military zones\n *  stateInput: {\n *     caseSensitive: false\n *  }\n *\n *  Case insensitive, include territories but not military zones\n *  stateInput: {\n *     includeTerritories: true\n *  }\n *\n *  Only allow capital letters, include territories and military zones\n *  stateInput: {\n *     caseSensitive: true,\n *     includeTerritories: true,\n *     includeMilitary: true\n *  }\n *\n *\n *\n */\n\n$.validator.addMethod(\"stateUS\", function(value, element, options) {\n\tvar isDefault = typeof options === \"undefined\",\n\t\tcaseSensitive = ( isDefault || typeof options.caseSensitive === \"undefined\" ) ? false : options.caseSensitive,\n\t\tincludeTerritories = ( isDefault || typeof options.includeTerritories === \"undefined\" ) ? false : options.includeTerritories,\n\t\tincludeMilitary = ( isDefault || typeof options.includeMilitary === \"undefined\" ) ? false : options.includeMilitary,\n\t\tregex;\n\n\tif (!includeTerritories && !includeMilitary) {\n\t\tregex = \"^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$\";\n\t} else if (includeTerritories && includeMilitary) {\n\t\tregex = \"^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$\";\n\t} else if (includeTerritories) {\n\t\tregex = \"^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$\";\n\t} else {\n\t\tregex = \"^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$\";\n\t}\n\n\tregex = caseSensitive ? new RegExp(regex) : new RegExp(regex, \"i\");\n\treturn this.optional(element) || regex.test(value);\n},\n\"Please specify a valid state\");\n\n// TODO check if value starts with <, otherwise don't try stripping anything\n$.validator.addMethod(\"strippedminlength\", function(value, element, param) {\n\treturn $(value).text().length >= param;\n}, $.validator.format(\"Please enter at least {0} characters\"));\n\n$.validator.addMethod(\"time\", function(value, element) {\n\treturn this.optional(element) || /^([01]\\d|2[0-3]|[0-9])(:[0-5]\\d){1,2}$/.test(value);\n}, \"Please enter a valid time, between 00:00 and 23:59\");\n\n$.validator.addMethod(\"time12h\", function(value, element) {\n\treturn this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\\d){1,2}(\\ ?[AP]M))$/i.test(value);\n}, \"Please enter a valid time in 12-hour am/pm format\");\n\n// same as url, but TLD is optional\n$.validator.addMethod(\"url2\", function(value, element) {\n\treturn this.optional(element) || /^(https?|ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)*(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?)(:\\d*)?)(\\/((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?(#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$/i.test(value);\n}, $.validator.messages.url);\n\n/**\n * Return true, if the value is a valid vehicle identification number (VIN).\n *\n * Works with all kind of text inputs.\n *\n * @example <input type=\"text\" size=\"20\" name=\"VehicleID\" class=\"{required:true,vinUS:true}\" />\n * @desc Declares a required input element whose value must be a valid vehicle identification number.\n *\n * @name $.validator.methods.vinUS\n * @type Boolean\n * @cat Plugins/Validate/Methods\n */\n$.validator.addMethod(\"vinUS\", function(v) {\n\tif (v.length !== 17) {\n\t\treturn false;\n\t}\n\n\tvar LL = [ \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"J\", \"K\", \"L\", \"M\", \"N\", \"P\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\" ],\n\t\tVL = [ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9 ],\n\t\tFL = [ 8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2 ],\n\t\trs = 0,\n\t\ti, n, d, f, cd, cdv;\n\n\tfor (i = 0; i < 17; i++) {\n\t\tf = FL[i];\n\t\td = v.slice(i, i + 1);\n\t\tif (i === 8) {\n\t\t\tcdv = d;\n\t\t}\n\t\tif (!isNaN(d)) {\n\t\t\td *= f;\n\t\t} else {\n\t\t\tfor (n = 0; n < LL.length; n++) {\n\t\t\t\tif (d.toUpperCase() === LL[n]) {\n\t\t\t\t\td = VL[n];\n\t\t\t\t\td *= f;\n\t\t\t\t\tif (isNaN(cdv) && n === 8) {\n\t\t\t\t\t\tcdv = LL[n];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\trs += d;\n\t}\n\tcd = rs % 11;\n\tif (cd === 10) {\n\t\tcd = \"X\";\n\t}\n\tif (cd === cdv) {\n\t\treturn true;\n\t}\n\treturn false;\n}, \"The specified vehicle identification number (VIN) is invalid.\");\n\n$.validator.addMethod(\"zipcodeUS\", function(value, element) {\n\treturn this.optional(element) || /^\\d{5}(-\\d{4})?$/.test(value);\n}, \"The specified US ZIP Code is invalid\");\n\n$.validator.addMethod(\"ziprange\", function(value, element) {\n\treturn this.optional(element) || /^90[2-5]\\d\\{2\\}-\\d{4}$/.test(value);\n}, \"Your ZIP-code must be in the range 902xx-xxxx to 905xx-xxxx\");\n\n}));"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/jquery-validation/dist/jquery.validate.js",
    "content": "/*!\n * jQuery Validation Plugin v1.14.0\n *\n * http://jqueryvalidation.org/\n *\n * Copyright (c) 2015 Jörn Zaefferer\n * Released under the MIT license\n */\n(function( factory ) {\n\tif ( typeof define === \"function\" && define.amd ) {\n\t\tdefine( [\"jquery\"], factory );\n\t} else {\n\t\tfactory( jQuery );\n\t}\n}(function( $ ) {\n\n$.extend($.fn, {\n\t// http://jqueryvalidation.org/validate/\n\tvalidate: function( options ) {\n\n\t\t// if nothing is selected, return nothing; can't chain anyway\n\t\tif ( !this.length ) {\n\t\t\tif ( options && options.debug && window.console ) {\n\t\t\t\tconsole.warn( \"Nothing selected, can't validate, returning nothing.\" );\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// check if a validator for this form was already created\n\t\tvar validator = $.data( this[ 0 ], \"validator\" );\n\t\tif ( validator ) {\n\t\t\treturn validator;\n\t\t}\n\n\t\t// Add novalidate tag if HTML5.\n\t\tthis.attr( \"novalidate\", \"novalidate\" );\n\n\t\tvalidator = new $.validator( options, this[ 0 ] );\n\t\t$.data( this[ 0 ], \"validator\", validator );\n\n\t\tif ( validator.settings.onsubmit ) {\n\n\t\t\tthis.on( \"click.validate\", \":submit\", function( event ) {\n\t\t\t\tif ( validator.settings.submitHandler ) {\n\t\t\t\t\tvalidator.submitButton = event.target;\n\t\t\t\t}\n\n\t\t\t\t// allow suppressing validation by adding a cancel class to the submit button\n\t\t\t\tif ( $( this ).hasClass( \"cancel\" ) ) {\n\t\t\t\t\tvalidator.cancelSubmit = true;\n\t\t\t\t}\n\n\t\t\t\t// allow suppressing validation by adding the html5 formnovalidate attribute to the submit button\n\t\t\t\tif ( $( this ).attr( \"formnovalidate\" ) !== undefined ) {\n\t\t\t\t\tvalidator.cancelSubmit = true;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// validate the form on submit\n\t\t\tthis.on( \"submit.validate\", function( event ) {\n\t\t\t\tif ( validator.settings.debug ) {\n\t\t\t\t\t// prevent form submit to be able to see console output\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t\tfunction handle() {\n\t\t\t\t\tvar hidden, result;\n\t\t\t\t\tif ( validator.settings.submitHandler ) {\n\t\t\t\t\t\tif ( validator.submitButton ) {\n\t\t\t\t\t\t\t// insert a hidden input as a replacement for the missing submit button\n\t\t\t\t\t\t\thidden = $( \"<input type='hidden'/>\" )\n\t\t\t\t\t\t\t\t.attr( \"name\", validator.submitButton.name )\n\t\t\t\t\t\t\t\t.val( $( validator.submitButton ).val() )\n\t\t\t\t\t\t\t\t.appendTo( validator.currentForm );\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresult = validator.settings.submitHandler.call( validator, validator.currentForm, event );\n\t\t\t\t\t\tif ( validator.submitButton ) {\n\t\t\t\t\t\t\t// and clean up afterwards; thanks to no-block-scope, hidden can be referenced\n\t\t\t\t\t\t\thidden.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( result !== undefined ) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// prevent submit for invalid forms or custom submit handlers\n\t\t\t\tif ( validator.cancelSubmit ) {\n\t\t\t\t\tvalidator.cancelSubmit = false;\n\t\t\t\t\treturn handle();\n\t\t\t\t}\n\t\t\t\tif ( validator.form() ) {\n\t\t\t\t\tif ( validator.pendingRequest ) {\n\t\t\t\t\t\tvalidator.formSubmitted = true;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn handle();\n\t\t\t\t} else {\n\t\t\t\t\tvalidator.focusInvalid();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treturn validator;\n\t},\n\t// http://jqueryvalidation.org/valid/\n\tvalid: function() {\n\t\tvar valid, validator, errorList;\n\n\t\tif ( $( this[ 0 ] ).is( \"form\" ) ) {\n\t\t\tvalid = this.validate().form();\n\t\t} else {\n\t\t\terrorList = [];\n\t\t\tvalid = true;\n\t\t\tvalidator = $( this[ 0 ].form ).validate();\n\t\t\tthis.each( function() {\n\t\t\t\tvalid = validator.element( this ) && valid;\n\t\t\t\terrorList = errorList.concat( validator.errorList );\n\t\t\t});\n\t\t\tvalidator.errorList = errorList;\n\t\t}\n\t\treturn valid;\n\t},\n\n\t// http://jqueryvalidation.org/rules/\n\trules: function( command, argument ) {\n\t\tvar element = this[ 0 ],\n\t\t\tsettings, staticRules, existingRules, data, param, filtered;\n\n\t\tif ( command ) {\n\t\t\tsettings = $.data( element.form, \"validator\" ).settings;\n\t\t\tstaticRules = settings.rules;\n\t\t\texistingRules = $.validator.staticRules( element );\n\t\t\tswitch ( command ) {\n\t\t\tcase \"add\":\n\t\t\t\t$.extend( existingRules, $.validator.normalizeRule( argument ) );\n\t\t\t\t// remove messages from rules, but allow them to be set separately\n\t\t\t\tdelete existingRules.messages;\n\t\t\t\tstaticRules[ element.name ] = existingRules;\n\t\t\t\tif ( argument.messages ) {\n\t\t\t\t\tsettings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"remove\":\n\t\t\t\tif ( !argument ) {\n\t\t\t\t\tdelete staticRules[ element.name ];\n\t\t\t\t\treturn existingRules;\n\t\t\t\t}\n\t\t\t\tfiltered = {};\n\t\t\t\t$.each( argument.split( /\\s/ ), function( index, method ) {\n\t\t\t\t\tfiltered[ method ] = existingRules[ method ];\n\t\t\t\t\tdelete existingRules[ method ];\n\t\t\t\t\tif ( method === \"required\" ) {\n\t\t\t\t\t\t$( element ).removeAttr( \"aria-required\" );\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\treturn filtered;\n\t\t\t}\n\t\t}\n\n\t\tdata = $.validator.normalizeRules(\n\t\t$.extend(\n\t\t\t{},\n\t\t\t$.validator.classRules( element ),\n\t\t\t$.validator.attributeRules( element ),\n\t\t\t$.validator.dataRules( element ),\n\t\t\t$.validator.staticRules( element )\n\t\t), element );\n\n\t\t// make sure required is at front\n\t\tif ( data.required ) {\n\t\t\tparam = data.required;\n\t\t\tdelete data.required;\n\t\t\tdata = $.extend( { required: param }, data );\n\t\t\t$( element ).attr( \"aria-required\", \"true\" );\n\t\t}\n\n\t\t// make sure remote is at back\n\t\tif ( data.remote ) {\n\t\t\tparam = data.remote;\n\t\t\tdelete data.remote;\n\t\t\tdata = $.extend( data, { remote: param });\n\t\t}\n\n\t\treturn data;\n\t}\n});\n\n// Custom selectors\n$.extend( $.expr[ \":\" ], {\n\t// http://jqueryvalidation.org/blank-selector/\n\tblank: function( a ) {\n\t\treturn !$.trim( \"\" + $( a ).val() );\n\t},\n\t// http://jqueryvalidation.org/filled-selector/\n\tfilled: function( a ) {\n\t\treturn !!$.trim( \"\" + $( a ).val() );\n\t},\n\t// http://jqueryvalidation.org/unchecked-selector/\n\tunchecked: function( a ) {\n\t\treturn !$( a ).prop( \"checked\" );\n\t}\n});\n\n// constructor for validator\n$.validator = function( options, form ) {\n\tthis.settings = $.extend( true, {}, $.validator.defaults, options );\n\tthis.currentForm = form;\n\tthis.init();\n};\n\n// http://jqueryvalidation.org/jQuery.validator.format/\n$.validator.format = function( source, params ) {\n\tif ( arguments.length === 1 ) {\n\t\treturn function() {\n\t\t\tvar args = $.makeArray( arguments );\n\t\t\targs.unshift( source );\n\t\t\treturn $.validator.format.apply( this, args );\n\t\t};\n\t}\n\tif ( arguments.length > 2 && params.constructor !== Array  ) {\n\t\tparams = $.makeArray( arguments ).slice( 1 );\n\t}\n\tif ( params.constructor !== Array ) {\n\t\tparams = [ params ];\n\t}\n\t$.each( params, function( i, n ) {\n\t\tsource = source.replace( new RegExp( \"\\\\{\" + i + \"\\\\}\", \"g\" ), function() {\n\t\t\treturn n;\n\t\t});\n\t});\n\treturn source;\n};\n\n$.extend( $.validator, {\n\n\tdefaults: {\n\t\tmessages: {},\n\t\tgroups: {},\n\t\trules: {},\n\t\terrorClass: \"error\",\n\t\tvalidClass: \"valid\",\n\t\terrorElement: \"label\",\n\t\tfocusCleanup: false,\n\t\tfocusInvalid: true,\n\t\terrorContainer: $( [] ),\n\t\terrorLabelContainer: $( [] ),\n\t\tonsubmit: true,\n\t\tignore: \":hidden\",\n\t\tignoreTitle: false,\n\t\tonfocusin: function( element ) {\n\t\t\tthis.lastActive = element;\n\n\t\t\t// Hide error label and remove error class on focus if enabled\n\t\t\tif ( this.settings.focusCleanup ) {\n\t\t\t\tif ( this.settings.unhighlight ) {\n\t\t\t\t\tthis.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );\n\t\t\t\t}\n\t\t\t\tthis.hideThese( this.errorsFor( element ) );\n\t\t\t}\n\t\t},\n\t\tonfocusout: function( element ) {\n\t\t\tif ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {\n\t\t\t\tthis.element( element );\n\t\t\t}\n\t\t},\n\t\tonkeyup: function( element, event ) {\n\t\t\t// Avoid revalidate the field when pressing one of the following keys\n\t\t\t// Shift       => 16\n\t\t\t// Ctrl        => 17\n\t\t\t// Alt         => 18\n\t\t\t// Caps lock   => 20\n\t\t\t// End         => 35\n\t\t\t// Home        => 36\n\t\t\t// Left arrow  => 37\n\t\t\t// Up arrow    => 38\n\t\t\t// Right arrow => 39\n\t\t\t// Down arrow  => 40\n\t\t\t// Insert      => 45\n\t\t\t// Num lock    => 144\n\t\t\t// AltGr key   => 225\n\t\t\tvar excludedKeys = [\n\t\t\t\t16, 17, 18, 20, 35, 36, 37,\n\t\t\t\t38, 39, 40, 45, 144, 225\n\t\t\t];\n\n\t\t\tif ( event.which === 9 && this.elementValue( element ) === \"\" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {\n\t\t\t\treturn;\n\t\t\t} else if ( element.name in this.submitted || element === this.lastElement ) {\n\t\t\t\tthis.element( element );\n\t\t\t}\n\t\t},\n\t\tonclick: function( element ) {\n\t\t\t// click on selects, radiobuttons and checkboxes\n\t\t\tif ( element.name in this.submitted ) {\n\t\t\t\tthis.element( element );\n\n\t\t\t// or option elements, check parent select in that case\n\t\t\t} else if ( element.parentNode.name in this.submitted ) {\n\t\t\t\tthis.element( element.parentNode );\n\t\t\t}\n\t\t},\n\t\thighlight: function( element, errorClass, validClass ) {\n\t\t\tif ( element.type === \"radio\" ) {\n\t\t\t\tthis.findByName( element.name ).addClass( errorClass ).removeClass( validClass );\n\t\t\t} else {\n\t\t\t\t$( element ).addClass( errorClass ).removeClass( validClass );\n\t\t\t}\n\t\t},\n\t\tunhighlight: function( element, errorClass, validClass ) {\n\t\t\tif ( element.type === \"radio\" ) {\n\t\t\t\tthis.findByName( element.name ).removeClass( errorClass ).addClass( validClass );\n\t\t\t} else {\n\t\t\t\t$( element ).removeClass( errorClass ).addClass( validClass );\n\t\t\t}\n\t\t}\n\t},\n\n\t// http://jqueryvalidation.org/jQuery.validator.setDefaults/\n\tsetDefaults: function( settings ) {\n\t\t$.extend( $.validator.defaults, settings );\n\t},\n\n\tmessages: {\n\t\trequired: \"This field is required.\",\n\t\tremote: \"Please fix this field.\",\n\t\temail: \"Please enter a valid email address.\",\n\t\turl: \"Please enter a valid URL.\",\n\t\tdate: \"Please enter a valid date.\",\n\t\tdateISO: \"Please enter a valid date ( ISO ).\",\n\t\tnumber: \"Please enter a valid number.\",\n\t\tdigits: \"Please enter only digits.\",\n\t\tcreditcard: \"Please enter a valid credit card number.\",\n\t\tequalTo: \"Please enter the same value again.\",\n\t\tmaxlength: $.validator.format( \"Please enter no more than {0} characters.\" ),\n\t\tminlength: $.validator.format( \"Please enter at least {0} characters.\" ),\n\t\trangelength: $.validator.format( \"Please enter a value between {0} and {1} characters long.\" ),\n\t\trange: $.validator.format( \"Please enter a value between {0} and {1}.\" ),\n\t\tmax: $.validator.format( \"Please enter a value less than or equal to {0}.\" ),\n\t\tmin: $.validator.format( \"Please enter a value greater than or equal to {0}.\" )\n\t},\n\n\tautoCreateRanges: false,\n\n\tprototype: {\n\n\t\tinit: function() {\n\t\t\tthis.labelContainer = $( this.settings.errorLabelContainer );\n\t\t\tthis.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm );\n\t\t\tthis.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );\n\t\t\tthis.submitted = {};\n\t\t\tthis.valueCache = {};\n\t\t\tthis.pendingRequest = 0;\n\t\t\tthis.pending = {};\n\t\t\tthis.invalid = {};\n\t\t\tthis.reset();\n\n\t\t\tvar groups = ( this.groups = {} ),\n\t\t\t\trules;\n\t\t\t$.each( this.settings.groups, function( key, value ) {\n\t\t\t\tif ( typeof value === \"string\" ) {\n\t\t\t\t\tvalue = value.split( /\\s/ );\n\t\t\t\t}\n\t\t\t\t$.each( value, function( index, name ) {\n\t\t\t\t\tgroups[ name ] = key;\n\t\t\t\t});\n\t\t\t});\n\t\t\trules = this.settings.rules;\n\t\t\t$.each( rules, function( key, value ) {\n\t\t\t\trules[ key ] = $.validator.normalizeRule( value );\n\t\t\t});\n\n\t\t\tfunction delegate( event ) {\n\t\t\t\tvar validator = $.data( this.form, \"validator\" ),\n\t\t\t\t\teventType = \"on\" + event.type.replace( /^validate/, \"\" ),\n\t\t\t\t\tsettings = validator.settings;\n\t\t\t\tif ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {\n\t\t\t\t\tsettings[ eventType ].call( validator, this, event );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$( this.currentForm )\n\t\t\t\t.on( \"focusin.validate focusout.validate keyup.validate\",\n\t\t\t\t\t\":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], \" +\n\t\t\t\t\t\"[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], \" +\n\t\t\t\t\t\"[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], \" +\n\t\t\t\t\t\"[type='radio'], [type='checkbox']\", delegate)\n\t\t\t\t// Support: Chrome, oldIE\n\t\t\t\t// \"select\" is provided as event.target when clicking a option\n\t\t\t\t.on(\"click.validate\", \"select, option, [type='radio'], [type='checkbox']\", delegate);\n\n\t\t\tif ( this.settings.invalidHandler ) {\n\t\t\t\t$( this.currentForm ).on( \"invalid-form.validate\", this.settings.invalidHandler );\n\t\t\t}\n\n\t\t\t// Add aria-required to any Static/Data/Class required fields before first validation\n\t\t\t// Screen readers require this attribute to be present before the initial submission http://www.w3.org/TR/WCAG-TECHS/ARIA2.html\n\t\t\t$( this.currentForm ).find( \"[required], [data-rule-required], .required\" ).attr( \"aria-required\", \"true\" );\n\t\t},\n\n\t\t// http://jqueryvalidation.org/Validator.form/\n\t\tform: function() {\n\t\t\tthis.checkForm();\n\t\t\t$.extend( this.submitted, this.errorMap );\n\t\t\tthis.invalid = $.extend({}, this.errorMap );\n\t\t\tif ( !this.valid() ) {\n\t\t\t\t$( this.currentForm ).triggerHandler( \"invalid-form\", [ this ]);\n\t\t\t}\n\t\t\tthis.showErrors();\n\t\t\treturn this.valid();\n\t\t},\n\n\t\tcheckForm: function() {\n\t\t\tthis.prepareForm();\n\t\t\tfor ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) {\n\t\t\t\tthis.check( elements[ i ] );\n\t\t\t}\n\t\t\treturn this.valid();\n\t\t},\n\n\t\t// http://jqueryvalidation.org/Validator.element/\n\t\telement: function( element ) {\n\t\t\tvar cleanElement = this.clean( element ),\n\t\t\t\tcheckElement = this.validationTargetFor( cleanElement ),\n\t\t\t\tresult = true;\n\n\t\t\tthis.lastElement = checkElement;\n\n\t\t\tif ( checkElement === undefined ) {\n\t\t\t\tdelete this.invalid[ cleanElement.name ];\n\t\t\t} else {\n\t\t\t\tthis.prepareElement( checkElement );\n\t\t\t\tthis.currentElements = $( checkElement );\n\n\t\t\t\tresult = this.check( checkElement ) !== false;\n\t\t\t\tif ( result ) {\n\t\t\t\t\tdelete this.invalid[ checkElement.name ];\n\t\t\t\t} else {\n\t\t\t\t\tthis.invalid[ checkElement.name ] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Add aria-invalid status for screen readers\n\t\t\t$( element ).attr( \"aria-invalid\", !result );\n\n\t\t\tif ( !this.numberOfInvalids() ) {\n\t\t\t\t// Hide error containers on last error\n\t\t\t\tthis.toHide = this.toHide.add( this.containers );\n\t\t\t}\n\t\t\tthis.showErrors();\n\t\t\treturn result;\n\t\t},\n\n\t\t// http://jqueryvalidation.org/Validator.showErrors/\n\t\tshowErrors: function( errors ) {\n\t\t\tif ( errors ) {\n\t\t\t\t// add items to error list and map\n\t\t\t\t$.extend( this.errorMap, errors );\n\t\t\t\tthis.errorList = [];\n\t\t\t\tfor ( var name in errors ) {\n\t\t\t\t\tthis.errorList.push({\n\t\t\t\t\t\tmessage: errors[ name ],\n\t\t\t\t\t\telement: this.findByName( name )[ 0 ]\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// remove items from success list\n\t\t\t\tthis.successList = $.grep( this.successList, function( element ) {\n\t\t\t\t\treturn !( element.name in errors );\n\t\t\t\t});\n\t\t\t}\n\t\t\tif ( this.settings.showErrors ) {\n\t\t\t\tthis.settings.showErrors.call( this, this.errorMap, this.errorList );\n\t\t\t} else {\n\t\t\t\tthis.defaultShowErrors();\n\t\t\t}\n\t\t},\n\n\t\t// http://jqueryvalidation.org/Validator.resetForm/\n\t\tresetForm: function() {\n\t\t\tif ( $.fn.resetForm ) {\n\t\t\t\t$( this.currentForm ).resetForm();\n\t\t\t}\n\t\t\tthis.submitted = {};\n\t\t\tthis.lastElement = null;\n\t\t\tthis.prepareForm();\n\t\t\tthis.hideErrors();\n\t\t\tvar i, elements = this.elements()\n\t\t\t\t.removeData( \"previousValue\" )\n\t\t\t\t.removeAttr( \"aria-invalid\" );\n\n\t\t\tif ( this.settings.unhighlight ) {\n\t\t\t\tfor ( i = 0; elements[ i ]; i++ ) {\n\t\t\t\t\tthis.settings.unhighlight.call( this, elements[ i ],\n\t\t\t\t\t\tthis.settings.errorClass, \"\" );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\telements.removeClass( this.settings.errorClass );\n\t\t\t}\n\t\t},\n\n\t\tnumberOfInvalids: function() {\n\t\t\treturn this.objectLength( this.invalid );\n\t\t},\n\n\t\tobjectLength: function( obj ) {\n\t\t\t/* jshint unused: false */\n\t\t\tvar count = 0,\n\t\t\t\ti;\n\t\t\tfor ( i in obj ) {\n\t\t\t\tcount++;\n\t\t\t}\n\t\t\treturn count;\n\t\t},\n\n\t\thideErrors: function() {\n\t\t\tthis.hideThese( this.toHide );\n\t\t},\n\n\t\thideThese: function( errors ) {\n\t\t\terrors.not( this.containers ).text( \"\" );\n\t\t\tthis.addWrapper( errors ).hide();\n\t\t},\n\n\t\tvalid: function() {\n\t\t\treturn this.size() === 0;\n\t\t},\n\n\t\tsize: function() {\n\t\t\treturn this.errorList.length;\n\t\t},\n\n\t\tfocusInvalid: function() {\n\t\t\tif ( this.settings.focusInvalid ) {\n\t\t\t\ttry {\n\t\t\t\t\t$( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [])\n\t\t\t\t\t.filter( \":visible\" )\n\t\t\t\t\t.focus()\n\t\t\t\t\t// manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find\n\t\t\t\t\t.trigger( \"focusin\" );\n\t\t\t\t} catch ( e ) {\n\t\t\t\t\t// ignore IE throwing errors when focusing hidden elements\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tfindLastActive: function() {\n\t\t\tvar lastActive = this.lastActive;\n\t\t\treturn lastActive && $.grep( this.errorList, function( n ) {\n\t\t\t\treturn n.element.name === lastActive.name;\n\t\t\t}).length === 1 && lastActive;\n\t\t},\n\n\t\telements: function() {\n\t\t\tvar validator = this,\n\t\t\t\trulesCache = {};\n\n\t\t\t// select all valid inputs inside the form (no submit or reset buttons)\n\t\t\treturn $( this.currentForm )\n\t\t\t.find( \"input, select, textarea\" )\n\t\t\t.not( \":submit, :reset, :image, :disabled\" )\n\t\t\t.not( this.settings.ignore )\n\t\t\t.filter( function() {\n\t\t\t\tif ( !this.name && validator.settings.debug && window.console ) {\n\t\t\t\t\tconsole.error( \"%o has no name assigned\", this );\n\t\t\t\t}\n\n\t\t\t\t// select only the first element for each name, and only those with rules specified\n\t\t\t\tif ( this.name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\trulesCache[ this.name ] = true;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t},\n\n\t\tclean: function( selector ) {\n\t\t\treturn $( selector )[ 0 ];\n\t\t},\n\n\t\terrors: function() {\n\t\t\tvar errorClass = this.settings.errorClass.split( \" \" ).join( \".\" );\n\t\t\treturn $( this.settings.errorElement + \".\" + errorClass, this.errorContext );\n\t\t},\n\n\t\treset: function() {\n\t\t\tthis.successList = [];\n\t\t\tthis.errorList = [];\n\t\t\tthis.errorMap = {};\n\t\t\tthis.toShow = $( [] );\n\t\t\tthis.toHide = $( [] );\n\t\t\tthis.currentElements = $( [] );\n\t\t},\n\n\t\tprepareForm: function() {\n\t\t\tthis.reset();\n\t\t\tthis.toHide = this.errors().add( this.containers );\n\t\t},\n\n\t\tprepareElement: function( element ) {\n\t\t\tthis.reset();\n\t\t\tthis.toHide = this.errorsFor( element );\n\t\t},\n\n\t\telementValue: function( element ) {\n\t\t\tvar val,\n\t\t\t\t$element = $( element ),\n\t\t\t\ttype = element.type;\n\n\t\t\tif ( type === \"radio\" || type === \"checkbox\" ) {\n\t\t\t\treturn this.findByName( element.name ).filter(\":checked\").val();\n\t\t\t} else if ( type === \"number\" && typeof element.validity !== \"undefined\" ) {\n\t\t\t\treturn element.validity.badInput ? false : $element.val();\n\t\t\t}\n\n\t\t\tval = $element.val();\n\t\t\tif ( typeof val === \"string\" ) {\n\t\t\t\treturn val.replace(/\\r/g, \"\" );\n\t\t\t}\n\t\t\treturn val;\n\t\t},\n\n\t\tcheck: function( element ) {\n\t\t\telement = this.validationTargetFor( this.clean( element ) );\n\n\t\t\tvar rules = $( element ).rules(),\n\t\t\t\trulesCount = $.map( rules, function( n, i ) {\n\t\t\t\t\treturn i;\n\t\t\t\t}).length,\n\t\t\t\tdependencyMismatch = false,\n\t\t\t\tval = this.elementValue( element ),\n\t\t\t\tresult, method, rule;\n\n\t\t\tfor ( method in rules ) {\n\t\t\t\trule = { method: method, parameters: rules[ method ] };\n\t\t\t\ttry {\n\n\t\t\t\t\tresult = $.validator.methods[ method ].call( this, val, element, rule.parameters );\n\n\t\t\t\t\t// if a method indicates that the field is optional and therefore valid,\n\t\t\t\t\t// don't mark it as valid when there are no other rules\n\t\t\t\t\tif ( result === \"dependency-mismatch\" && rulesCount === 1 ) {\n\t\t\t\t\t\tdependencyMismatch = true;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tdependencyMismatch = false;\n\n\t\t\t\t\tif ( result === \"pending\" ) {\n\t\t\t\t\t\tthis.toHide = this.toHide.not( this.errorsFor( element ) );\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( !result ) {\n\t\t\t\t\t\tthis.formatAndAdd( element, rule );\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t} catch ( e ) {\n\t\t\t\t\tif ( this.settings.debug && window.console ) {\n\t\t\t\t\t\tconsole.log( \"Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\", e );\n\t\t\t\t\t}\n\t\t\t\t\tif ( e instanceof TypeError ) {\n\t\t\t\t\t\te.message += \".  Exception occurred when checking element \" + element.id + \", check the '\" + rule.method + \"' method.\";\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( dependencyMismatch ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ( this.objectLength( rules ) ) {\n\t\t\t\tthis.successList.push( element );\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\t// return the custom message for the given element and validation method\n\t\t// specified in the element's HTML5 data attribute\n\t\t// return the generic message if present and no method specific message is present\n\t\tcustomDataMessage: function( element, method ) {\n\t\t\treturn $( element ).data( \"msg\" + method.charAt( 0 ).toUpperCase() +\n\t\t\t\tmethod.substring( 1 ).toLowerCase() ) || $( element ).data( \"msg\" );\n\t\t},\n\n\t\t// return the custom message for the given element name and validation method\n\t\tcustomMessage: function( name, method ) {\n\t\t\tvar m = this.settings.messages[ name ];\n\t\t\treturn m && ( m.constructor === String ? m : m[ method ]);\n\t\t},\n\n\t\t// return the first defined argument, allowing empty strings\n\t\tfindDefined: function() {\n\t\t\tfor ( var i = 0; i < arguments.length; i++) {\n\t\t\t\tif ( arguments[ i ] !== undefined ) {\n\t\t\t\t\treturn arguments[ i ];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t},\n\n\t\tdefaultMessage: function( element, method ) {\n\t\t\treturn this.findDefined(\n\t\t\t\tthis.customMessage( element.name, method ),\n\t\t\t\tthis.customDataMessage( element, method ),\n\t\t\t\t// title is never undefined, so handle empty string as undefined\n\t\t\t\t!this.settings.ignoreTitle && element.title || undefined,\n\t\t\t\t$.validator.messages[ method ],\n\t\t\t\t\"<strong>Warning: No message defined for \" + element.name + \"</strong>\"\n\t\t\t);\n\t\t},\n\n\t\tformatAndAdd: function( element, rule ) {\n\t\t\tvar message = this.defaultMessage( element, rule.method ),\n\t\t\t\ttheregex = /\\$?\\{(\\d+)\\}/g;\n\t\t\tif ( typeof message === \"function\" ) {\n\t\t\t\tmessage = message.call( this, rule.parameters, element );\n\t\t\t} else if ( theregex.test( message ) ) {\n\t\t\t\tmessage = $.validator.format( message.replace( theregex, \"{$1}\" ), rule.parameters );\n\t\t\t}\n\t\t\tthis.errorList.push({\n\t\t\t\tmessage: message,\n\t\t\t\telement: element,\n\t\t\t\tmethod: rule.method\n\t\t\t});\n\n\t\t\tthis.errorMap[ element.name ] = message;\n\t\t\tthis.submitted[ element.name ] = message;\n\t\t},\n\n\t\taddWrapper: function( toToggle ) {\n\t\t\tif ( this.settings.wrapper ) {\n\t\t\t\ttoToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );\n\t\t\t}\n\t\t\treturn toToggle;\n\t\t},\n\n\t\tdefaultShowErrors: function() {\n\t\t\tvar i, elements, error;\n\t\t\tfor ( i = 0; this.errorList[ i ]; i++ ) {\n\t\t\t\terror = this.errorList[ i ];\n\t\t\t\tif ( this.settings.highlight ) {\n\t\t\t\t\tthis.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );\n\t\t\t\t}\n\t\t\t\tthis.showLabel( error.element, error.message );\n\t\t\t}\n\t\t\tif ( this.errorList.length ) {\n\t\t\t\tthis.toShow = this.toShow.add( this.containers );\n\t\t\t}\n\t\t\tif ( this.settings.success ) {\n\t\t\t\tfor ( i = 0; this.successList[ i ]; i++ ) {\n\t\t\t\t\tthis.showLabel( this.successList[ i ] );\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( this.settings.unhighlight ) {\n\t\t\t\tfor ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {\n\t\t\t\t\tthis.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.toHide = this.toHide.not( this.toShow );\n\t\t\tthis.hideErrors();\n\t\t\tthis.addWrapper( this.toShow ).show();\n\t\t},\n\n\t\tvalidElements: function() {\n\t\t\treturn this.currentElements.not( this.invalidElements() );\n\t\t},\n\n\t\tinvalidElements: function() {\n\t\t\treturn $( this.errorList ).map(function() {\n\t\t\t\treturn this.element;\n\t\t\t});\n\t\t},\n\n\t\tshowLabel: function( element, message ) {\n\t\t\tvar place, group, errorID,\n\t\t\t\terror = this.errorsFor( element ),\n\t\t\t\telementID = this.idOrName( element ),\n\t\t\t\tdescribedBy = $( element ).attr( \"aria-describedby\" );\n\t\t\tif ( error.length ) {\n\t\t\t\t// refresh error/success class\n\t\t\t\terror.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );\n\t\t\t\t// replace message on existing label\n\t\t\t\terror.html( message );\n\t\t\t} else {\n\t\t\t\t// create error element\n\t\t\t\terror = $( \"<\" + this.settings.errorElement + \">\" )\n\t\t\t\t\t.attr( \"id\", elementID + \"-error\" )\n\t\t\t\t\t.addClass( this.settings.errorClass )\n\t\t\t\t\t.html( message || \"\" );\n\n\t\t\t\t// Maintain reference to the element to be placed into the DOM\n\t\t\t\tplace = error;\n\t\t\t\tif ( this.settings.wrapper ) {\n\t\t\t\t\t// make sure the element is visible, even in IE\n\t\t\t\t\t// actually showing the wrapped element is handled elsewhere\n\t\t\t\t\tplace = error.hide().show().wrap( \"<\" + this.settings.wrapper + \"/>\" ).parent();\n\t\t\t\t}\n\t\t\t\tif ( this.labelContainer.length ) {\n\t\t\t\t\tthis.labelContainer.append( place );\n\t\t\t\t} else if ( this.settings.errorPlacement ) {\n\t\t\t\t\tthis.settings.errorPlacement( place, $( element ) );\n\t\t\t\t} else {\n\t\t\t\t\tplace.insertAfter( element );\n\t\t\t\t}\n\n\t\t\t\t// Link error back to the element\n\t\t\t\tif ( error.is( \"label\" ) ) {\n\t\t\t\t\t// If the error is a label, then associate using 'for'\n\t\t\t\t\terror.attr( \"for\", elementID );\n\t\t\t\t} else if ( error.parents( \"label[for='\" + elementID + \"']\" ).length === 0 ) {\n\t\t\t\t\t// If the element is not a child of an associated label, then it's necessary\n\t\t\t\t\t// to explicitly apply aria-describedby\n\n\t\t\t\t\terrorID = error.attr( \"id\" ).replace( /(:|\\.|\\[|\\]|\\$)/g, \"\\\\$1\");\n\t\t\t\t\t// Respect existing non-error aria-describedby\n\t\t\t\t\tif ( !describedBy ) {\n\t\t\t\t\t\tdescribedBy = errorID;\n\t\t\t\t\t} else if ( !describedBy.match( new RegExp( \"\\\\b\" + errorID + \"\\\\b\" ) ) ) {\n\t\t\t\t\t\t// Add to end of list if not already present\n\t\t\t\t\t\tdescribedBy += \" \" + errorID;\n\t\t\t\t\t}\n\t\t\t\t\t$( element ).attr( \"aria-describedby\", describedBy );\n\n\t\t\t\t\t// If this element is grouped, then assign to all elements in the same group\n\t\t\t\t\tgroup = this.groups[ element.name ];\n\t\t\t\t\tif ( group ) {\n\t\t\t\t\t\t$.each( this.groups, function( name, testgroup ) {\n\t\t\t\t\t\t\tif ( testgroup === group ) {\n\t\t\t\t\t\t\t\t$( \"[name='\" + name + \"']\", this.currentForm )\n\t\t\t\t\t\t\t\t\t.attr( \"aria-describedby\", error.attr( \"id\" ) );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( !message && this.settings.success ) {\n\t\t\t\terror.text( \"\" );\n\t\t\t\tif ( typeof this.settings.success === \"string\" ) {\n\t\t\t\t\terror.addClass( this.settings.success );\n\t\t\t\t} else {\n\t\t\t\t\tthis.settings.success( error, element );\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.toShow = this.toShow.add( error );\n\t\t},\n\n\t\terrorsFor: function( element ) {\n\t\t\tvar name = this.idOrName( element ),\n\t\t\t\tdescriber = $( element ).attr( \"aria-describedby\" ),\n\t\t\t\tselector = \"label[for='\" + name + \"'], label[for='\" + name + \"'] *\";\n\n\t\t\t// aria-describedby should directly reference the error element\n\t\t\tif ( describer ) {\n\t\t\t\tselector = selector + \", #\" + describer.replace( /\\s+/g, \", #\" );\n\t\t\t}\n\t\t\treturn this\n\t\t\t\t.errors()\n\t\t\t\t.filter( selector );\n\t\t},\n\n\t\tidOrName: function( element ) {\n\t\t\treturn this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name );\n\t\t},\n\n\t\tvalidationTargetFor: function( element ) {\n\n\t\t\t// If radio/checkbox, validate first element in group instead\n\t\t\tif ( this.checkable( element ) ) {\n\t\t\t\telement = this.findByName( element.name );\n\t\t\t}\n\n\t\t\t// Always apply ignore filter\n\t\t\treturn $( element ).not( this.settings.ignore )[ 0 ];\n\t\t},\n\n\t\tcheckable: function( element ) {\n\t\t\treturn ( /radio|checkbox/i ).test( element.type );\n\t\t},\n\n\t\tfindByName: function( name ) {\n\t\t\treturn $( this.currentForm ).find( \"[name='\" + name + \"']\" );\n\t\t},\n\n\t\tgetLength: function( value, element ) {\n\t\t\tswitch ( element.nodeName.toLowerCase() ) {\n\t\t\tcase \"select\":\n\t\t\t\treturn $( \"option:selected\", element ).length;\n\t\t\tcase \"input\":\n\t\t\t\tif ( this.checkable( element ) ) {\n\t\t\t\t\treturn this.findByName( element.name ).filter( \":checked\" ).length;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn value.length;\n\t\t},\n\n\t\tdepend: function( param, element ) {\n\t\t\treturn this.dependTypes[typeof param] ? this.dependTypes[typeof param]( param, element ) : true;\n\t\t},\n\n\t\tdependTypes: {\n\t\t\t\"boolean\": function( param ) {\n\t\t\t\treturn param;\n\t\t\t},\n\t\t\t\"string\": function( param, element ) {\n\t\t\t\treturn !!$( param, element.form ).length;\n\t\t\t},\n\t\t\t\"function\": function( param, element ) {\n\t\t\t\treturn param( element );\n\t\t\t}\n\t\t},\n\n\t\toptional: function( element ) {\n\t\t\tvar val = this.elementValue( element );\n\t\t\treturn !$.validator.methods.required.call( this, val, element ) && \"dependency-mismatch\";\n\t\t},\n\n\t\tstartRequest: function( element ) {\n\t\t\tif ( !this.pending[ element.name ] ) {\n\t\t\t\tthis.pendingRequest++;\n\t\t\t\tthis.pending[ element.name ] = true;\n\t\t\t}\n\t\t},\n\n\t\tstopRequest: function( element, valid ) {\n\t\t\tthis.pendingRequest--;\n\t\t\t// sometimes synchronization fails, make sure pendingRequest is never < 0\n\t\t\tif ( this.pendingRequest < 0 ) {\n\t\t\t\tthis.pendingRequest = 0;\n\t\t\t}\n\t\t\tdelete this.pending[ element.name ];\n\t\t\tif ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) {\n\t\t\t\t$( this.currentForm ).submit();\n\t\t\t\tthis.formSubmitted = false;\n\t\t\t} else if (!valid && this.pendingRequest === 0 && this.formSubmitted ) {\n\t\t\t\t$( this.currentForm ).triggerHandler( \"invalid-form\", [ this ]);\n\t\t\t\tthis.formSubmitted = false;\n\t\t\t}\n\t\t},\n\n\t\tpreviousValue: function( element ) {\n\t\t\treturn $.data( element, \"previousValue\" ) || $.data( element, \"previousValue\", {\n\t\t\t\told: null,\n\t\t\t\tvalid: true,\n\t\t\t\tmessage: this.defaultMessage( element, \"remote\" )\n\t\t\t});\n\t\t},\n\n\t\t// cleans up all forms and elements, removes validator-specific events\n\t\tdestroy: function() {\n\t\t\tthis.resetForm();\n\n\t\t\t$( this.currentForm )\n\t\t\t\t.off( \".validate\" )\n\t\t\t\t.removeData( \"validator\" );\n\t\t}\n\n\t},\n\n\tclassRuleSettings: {\n\t\trequired: { required: true },\n\t\temail: { email: true },\n\t\turl: { url: true },\n\t\tdate: { date: true },\n\t\tdateISO: { dateISO: true },\n\t\tnumber: { number: true },\n\t\tdigits: { digits: true },\n\t\tcreditcard: { creditcard: true }\n\t},\n\n\taddClassRules: function( className, rules ) {\n\t\tif ( className.constructor === String ) {\n\t\t\tthis.classRuleSettings[ className ] = rules;\n\t\t} else {\n\t\t\t$.extend( this.classRuleSettings, className );\n\t\t}\n\t},\n\n\tclassRules: function( element ) {\n\t\tvar rules = {},\n\t\t\tclasses = $( element ).attr( \"class\" );\n\n\t\tif ( classes ) {\n\t\t\t$.each( classes.split( \" \" ), function() {\n\t\t\t\tif ( this in $.validator.classRuleSettings ) {\n\t\t\t\t\t$.extend( rules, $.validator.classRuleSettings[ this ]);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn rules;\n\t},\n\n\tnormalizeAttributeRule: function( rules, type, method, value ) {\n\n\t\t// convert the value to a number for number inputs, and for text for backwards compability\n\t\t// allows type=\"date\" and others to be compared as strings\n\t\tif ( /min|max/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {\n\t\t\tvalue = Number( value );\n\n\t\t\t// Support Opera Mini, which returns NaN for undefined minlength\n\t\t\tif ( isNaN( value ) ) {\n\t\t\t\tvalue = undefined;\n\t\t\t}\n\t\t}\n\n\t\tif ( value || value === 0 ) {\n\t\t\trules[ method ] = value;\n\t\t} else if ( type === method && type !== \"range\" ) {\n\n\t\t\t// exception: the jquery validate 'range' method\n\t\t\t// does not test for the html5 'range' type\n\t\t\trules[ method ] = true;\n\t\t}\n\t},\n\n\tattributeRules: function( element ) {\n\t\tvar rules = {},\n\t\t\t$element = $( element ),\n\t\t\ttype = element.getAttribute( \"type\" ),\n\t\t\tmethod, value;\n\n\t\tfor ( method in $.validator.methods ) {\n\n\t\t\t// support for <input required> in both html5 and older browsers\n\t\t\tif ( method === \"required\" ) {\n\t\t\t\tvalue = element.getAttribute( method );\n\n\t\t\t\t// Some browsers return an empty string for the required attribute\n\t\t\t\t// and non-HTML5 browsers might have required=\"\" markup\n\t\t\t\tif ( value === \"\" ) {\n\t\t\t\t\tvalue = true;\n\t\t\t\t}\n\n\t\t\t\t// force non-HTML5 browsers to return bool\n\t\t\t\tvalue = !!value;\n\t\t\t} else {\n\t\t\t\tvalue = $element.attr( method );\n\t\t\t}\n\n\t\t\tthis.normalizeAttributeRule( rules, type, method, value );\n\t\t}\n\n\t\t// maxlength may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs\n\t\tif ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {\n\t\t\tdelete rules.maxlength;\n\t\t}\n\n\t\treturn rules;\n\t},\n\n\tdataRules: function( element ) {\n\t\tvar rules = {},\n\t\t\t$element = $( element ),\n\t\t\ttype = element.getAttribute( \"type\" ),\n\t\t\tmethod, value;\n\n\t\tfor ( method in $.validator.methods ) {\n\t\t\tvalue = $element.data( \"rule\" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );\n\t\t\tthis.normalizeAttributeRule( rules, type, method, value );\n\t\t}\n\t\treturn rules;\n\t},\n\n\tstaticRules: function( element ) {\n\t\tvar rules = {},\n\t\t\tvalidator = $.data( element.form, \"validator\" );\n\n\t\tif ( validator.settings.rules ) {\n\t\t\trules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};\n\t\t}\n\t\treturn rules;\n\t},\n\n\tnormalizeRules: function( rules, element ) {\n\t\t// handle dependency check\n\t\t$.each( rules, function( prop, val ) {\n\t\t\t// ignore rule when param is explicitly false, eg. required:false\n\t\t\tif ( val === false ) {\n\t\t\t\tdelete rules[ prop ];\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ( val.param || val.depends ) {\n\t\t\t\tvar keepRule = true;\n\t\t\t\tswitch ( typeof val.depends ) {\n\t\t\t\tcase \"string\":\n\t\t\t\t\tkeepRule = !!$( val.depends, element.form ).length;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"function\":\n\t\t\t\t\tkeepRule = val.depends.call( element, element );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif ( keepRule ) {\n\t\t\t\t\trules[ prop ] = val.param !== undefined ? val.param : true;\n\t\t\t\t} else {\n\t\t\t\t\tdelete rules[ prop ];\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// evaluate parameters\n\t\t$.each( rules, function( rule, parameter ) {\n\t\t\trules[ rule ] = $.isFunction( parameter ) ? parameter( element ) : parameter;\n\t\t});\n\n\t\t// clean number parameters\n\t\t$.each([ \"minlength\", \"maxlength\" ], function() {\n\t\t\tif ( rules[ this ] ) {\n\t\t\t\trules[ this ] = Number( rules[ this ] );\n\t\t\t}\n\t\t});\n\t\t$.each([ \"rangelength\", \"range\" ], function() {\n\t\t\tvar parts;\n\t\t\tif ( rules[ this ] ) {\n\t\t\t\tif ( $.isArray( rules[ this ] ) ) {\n\t\t\t\t\trules[ this ] = [ Number( rules[ this ][ 0 ]), Number( rules[ this ][ 1 ] ) ];\n\t\t\t\t} else if ( typeof rules[ this ] === \"string\" ) {\n\t\t\t\t\tparts = rules[ this ].replace(/[\\[\\]]/g, \"\" ).split( /[\\s,]+/ );\n\t\t\t\t\trules[ this ] = [ Number( parts[ 0 ]), Number( parts[ 1 ] ) ];\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tif ( $.validator.autoCreateRanges ) {\n\t\t\t// auto-create ranges\n\t\t\tif ( rules.min != null && rules.max != null ) {\n\t\t\t\trules.range = [ rules.min, rules.max ];\n\t\t\t\tdelete rules.min;\n\t\t\t\tdelete rules.max;\n\t\t\t}\n\t\t\tif ( rules.minlength != null && rules.maxlength != null ) {\n\t\t\t\trules.rangelength = [ rules.minlength, rules.maxlength ];\n\t\t\t\tdelete rules.minlength;\n\t\t\t\tdelete rules.maxlength;\n\t\t\t}\n\t\t}\n\n\t\treturn rules;\n\t},\n\n\t// Converts a simple string to a {string: true} rule, e.g., \"required\" to {required:true}\n\tnormalizeRule: function( data ) {\n\t\tif ( typeof data === \"string\" ) {\n\t\t\tvar transformed = {};\n\t\t\t$.each( data.split( /\\s/ ), function() {\n\t\t\t\ttransformed[ this ] = true;\n\t\t\t});\n\t\t\tdata = transformed;\n\t\t}\n\t\treturn data;\n\t},\n\n\t// http://jqueryvalidation.org/jQuery.validator.addMethod/\n\taddMethod: function( name, method, message ) {\n\t\t$.validator.methods[ name ] = method;\n\t\t$.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ];\n\t\tif ( method.length < 3 ) {\n\t\t\t$.validator.addClassRules( name, $.validator.normalizeRule( name ) );\n\t\t}\n\t},\n\n\tmethods: {\n\n\t\t// http://jqueryvalidation.org/required-method/\n\t\trequired: function( value, element, param ) {\n\t\t\t// check if dependency is met\n\t\t\tif ( !this.depend( param, element ) ) {\n\t\t\t\treturn \"dependency-mismatch\";\n\t\t\t}\n\t\t\tif ( element.nodeName.toLowerCase() === \"select\" ) {\n\t\t\t\t// could be an array for select-multiple or a string, both are fine this way\n\t\t\t\tvar val = $( element ).val();\n\t\t\t\treturn val && val.length > 0;\n\t\t\t}\n\t\t\tif ( this.checkable( element ) ) {\n\t\t\t\treturn this.getLength( value, element ) > 0;\n\t\t\t}\n\t\t\treturn value.length > 0;\n\t\t},\n\n\t\t// http://jqueryvalidation.org/email-method/\n\t\temail: function( value, element ) {\n\t\t\t// From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address\n\t\t\t// Retrieved 2014-01-14\n\t\t\t// If you have a problem with this implementation, report a bug against the above spec\n\t\t\t// Or use custom methods to implement your own email validation\n\t\t\treturn this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value );\n\t\t},\n\n\t\t// http://jqueryvalidation.org/url-method/\n\t\turl: function( value, element ) {\n\n\t\t\t// Copyright (c) 2010-2013 Diego Perini, MIT licensed\n\t\t\t// https://gist.github.com/dperini/729294\n\t\t\t// see also https://mathiasbynens.be/demo/url-regex\n\t\t\t// modified to allow protocol-relative URLs\n\t\t\treturn this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})).?)(?::\\d{2,5})?(?:[/?#]\\S*)?$/i.test( value );\n\t\t},\n\n\t\t// http://jqueryvalidation.org/date-method/\n\t\tdate: function( value, element ) {\n\t\t\treturn this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );\n\t\t},\n\n\t\t// http://jqueryvalidation.org/dateISO-method/\n\t\tdateISO: function( value, element ) {\n\t\t\treturn this.optional( element ) || /^\\d{4}[\\/\\-](0?[1-9]|1[012])[\\/\\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );\n\t\t},\n\n\t\t// http://jqueryvalidation.org/number-method/\n\t\tnumber: function( value, element ) {\n\t\t\treturn this.optional( element ) || /^(?:-?\\d+|-?\\d{1,3}(?:,\\d{3})+)?(?:\\.\\d+)?$/.test( value );\n\t\t},\n\n\t\t// http://jqueryvalidation.org/digits-method/\n\t\tdigits: function( value, element ) {\n\t\t\treturn this.optional( element ) || /^\\d+$/.test( value );\n\t\t},\n\n\t\t// http://jqueryvalidation.org/creditcard-method/\n\t\t// based on http://en.wikipedia.org/wiki/Luhn_algorithm\n\t\tcreditcard: function( value, element ) {\n\t\t\tif ( this.optional( element ) ) {\n\t\t\t\treturn \"dependency-mismatch\";\n\t\t\t}\n\t\t\t// accept only spaces, digits and dashes\n\t\t\tif ( /[^0-9 \\-]+/.test( value ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvar nCheck = 0,\n\t\t\t\tnDigit = 0,\n\t\t\t\tbEven = false,\n\t\t\t\tn, cDigit;\n\n\t\t\tvalue = value.replace( /\\D/g, \"\" );\n\n\t\t\t// Basing min and max length on\n\t\t\t// http://developer.ean.com/general_info/Valid_Credit_Card_Types\n\t\t\tif ( value.length < 13 || value.length > 19 ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfor ( n = value.length - 1; n >= 0; n--) {\n\t\t\t\tcDigit = value.charAt( n );\n\t\t\t\tnDigit = parseInt( cDigit, 10 );\n\t\t\t\tif ( bEven ) {\n\t\t\t\t\tif ( ( nDigit *= 2 ) > 9 ) {\n\t\t\t\t\t\tnDigit -= 9;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnCheck += nDigit;\n\t\t\t\tbEven = !bEven;\n\t\t\t}\n\n\t\t\treturn ( nCheck % 10 ) === 0;\n\t\t},\n\n\t\t// http://jqueryvalidation.org/minlength-method/\n\t\tminlength: function( value, element, param ) {\n\t\t\tvar length = $.isArray( value ) ? value.length : this.getLength( value, element );\n\t\t\treturn this.optional( element ) || length >= param;\n\t\t},\n\n\t\t// http://jqueryvalidation.org/maxlength-method/\n\t\tmaxlength: function( value, element, param ) {\n\t\t\tvar length = $.isArray( value ) ? value.length : this.getLength( value, element );\n\t\t\treturn this.optional( element ) || length <= param;\n\t\t},\n\n\t\t// http://jqueryvalidation.org/rangelength-method/\n\t\trangelength: function( value, element, param ) {\n\t\t\tvar length = $.isArray( value ) ? value.length : this.getLength( value, element );\n\t\t\treturn this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );\n\t\t},\n\n\t\t// http://jqueryvalidation.org/min-method/\n\t\tmin: function( value, element, param ) {\n\t\t\treturn this.optional( element ) || value >= param;\n\t\t},\n\n\t\t// http://jqueryvalidation.org/max-method/\n\t\tmax: function( value, element, param ) {\n\t\t\treturn this.optional( element ) || value <= param;\n\t\t},\n\n\t\t// http://jqueryvalidation.org/range-method/\n\t\trange: function( value, element, param ) {\n\t\t\treturn this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );\n\t\t},\n\n\t\t// http://jqueryvalidation.org/equalTo-method/\n\t\tequalTo: function( value, element, param ) {\n\t\t\t// bind to the blur event of the target in order to revalidate whenever the target field is updated\n\t\t\t// TODO find a way to bind the event just once, avoiding the unbind-rebind overhead\n\t\t\tvar target = $( param );\n\t\t\tif ( this.settings.onfocusout ) {\n\t\t\t\ttarget.off( \".validate-equalTo\" ).on( \"blur.validate-equalTo\", function() {\n\t\t\t\t\t$( element ).valid();\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn value === target.val();\n\t\t},\n\n\t\t// http://jqueryvalidation.org/remote-method/\n\t\tremote: function( value, element, param ) {\n\t\t\tif ( this.optional( element ) ) {\n\t\t\t\treturn \"dependency-mismatch\";\n\t\t\t}\n\n\t\t\tvar previous = this.previousValue( element ),\n\t\t\t\tvalidator, data;\n\n\t\t\tif (!this.settings.messages[ element.name ] ) {\n\t\t\t\tthis.settings.messages[ element.name ] = {};\n\t\t\t}\n\t\t\tprevious.originalMessage = this.settings.messages[ element.name ].remote;\n\t\t\tthis.settings.messages[ element.name ].remote = previous.message;\n\n\t\t\tparam = typeof param === \"string\" && { url: param } || param;\n\n\t\t\tif ( previous.old === value ) {\n\t\t\t\treturn previous.valid;\n\t\t\t}\n\n\t\t\tprevious.old = value;\n\t\t\tvalidator = this;\n\t\t\tthis.startRequest( element );\n\t\t\tdata = {};\n\t\t\tdata[ element.name ] = value;\n\t\t\t$.ajax( $.extend( true, {\n\t\t\t\tmode: \"abort\",\n\t\t\t\tport: \"validate\" + element.name,\n\t\t\t\tdataType: \"json\",\n\t\t\t\tdata: data,\n\t\t\t\tcontext: validator.currentForm,\n\t\t\t\tsuccess: function( response ) {\n\t\t\t\t\tvar valid = response === true || response === \"true\",\n\t\t\t\t\t\terrors, message, submitted;\n\n\t\t\t\t\tvalidator.settings.messages[ element.name ].remote = previous.originalMessage;\n\t\t\t\t\tif ( valid ) {\n\t\t\t\t\t\tsubmitted = validator.formSubmitted;\n\t\t\t\t\t\tvalidator.prepareElement( element );\n\t\t\t\t\t\tvalidator.formSubmitted = submitted;\n\t\t\t\t\t\tvalidator.successList.push( element );\n\t\t\t\t\t\tdelete validator.invalid[ element.name ];\n\t\t\t\t\t\tvalidator.showErrors();\n\t\t\t\t\t} else {\n\t\t\t\t\t\terrors = {};\n\t\t\t\t\t\tmessage = response || validator.defaultMessage( element, \"remote\" );\n\t\t\t\t\t\terrors[ element.name ] = previous.message = $.isFunction( message ) ? message( value ) : message;\n\t\t\t\t\t\tvalidator.invalid[ element.name ] = true;\n\t\t\t\t\t\tvalidator.showErrors( errors );\n\t\t\t\t\t}\n\t\t\t\t\tprevious.valid = valid;\n\t\t\t\t\tvalidator.stopRequest( element, valid );\n\t\t\t\t}\n\t\t\t}, param ) );\n\t\t\treturn \"pending\";\n\t\t}\n\t}\n\n});\n\n// ajax mode: abort\n// usage: $.ajax({ mode: \"abort\"[, port: \"uniqueport\"]});\n// if mode:\"abort\" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()\n\nvar pendingRequests = {},\n\tajax;\n// Use a prefilter if available (1.5+)\nif ( $.ajaxPrefilter ) {\n\t$.ajaxPrefilter(function( settings, _, xhr ) {\n\t\tvar port = settings.port;\n\t\tif ( settings.mode === \"abort\" ) {\n\t\t\tif ( pendingRequests[port] ) {\n\t\t\t\tpendingRequests[port].abort();\n\t\t\t}\n\t\t\tpendingRequests[port] = xhr;\n\t\t}\n\t});\n} else {\n\t// Proxy ajax\n\tajax = $.ajax;\n\t$.ajax = function( settings ) {\n\t\tvar mode = ( \"mode\" in settings ? settings : $.ajaxSettings ).mode,\n\t\t\tport = ( \"port\" in settings ? settings : $.ajaxSettings ).port;\n\t\tif ( mode === \"abort\" ) {\n\t\t\tif ( pendingRequests[port] ) {\n\t\t\t\tpendingRequests[port].abort();\n\t\t\t}\n\t\t\tpendingRequests[port] = ajax.apply(this, arguments);\n\t\t\treturn pendingRequests[port];\n\t\t}\n\t\treturn ajax.apply(this, arguments);\n\t};\n}\n\n}));"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/jquery-validation-unobtrusive/.bower.json",
    "content": "{\n  \"name\": \"jquery-validation-unobtrusive\",\n  \"version\": \"3.2.6\",\n  \"homepage\": \"https://github.com/aspnet/jquery-validation-unobtrusive\",\n  \"description\": \"Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.\",\n  \"main\": [\n    \"jquery.validate.unobtrusive.js\"\n  ],\n  \"ignore\": [\n    \"**/.*\",\n    \"*.json\",\n    \"*.md\",\n    \"*.txt\",\n    \"gulpfile.js\"\n  ],\n  \"keywords\": [\n    \"jquery\",\n    \"asp.net\",\n    \"mvc\",\n    \"validation\",\n    \"unobtrusive\"\n  ],\n  \"authors\": [\n    \"Microsoft\"\n  ],\n  \"license\": \"http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/aspnet/jquery-validation-unobtrusive.git\"\n  },\n  \"dependencies\": {\n    \"jquery-validation\": \">=1.8\",\n    \"jquery\": \">=1.8\"\n  },\n  \"_release\": \"3.2.6\",\n  \"_resolution\": {\n    \"type\": \"version\",\n    \"tag\": \"v3.2.6\",\n    \"commit\": \"13386cd1b5947d8a5d23a12b531ce3960be1eba7\"\n  },\n  \"_source\": \"git://github.com/aspnet/jquery-validation-unobtrusive.git\",\n  \"_target\": \"3.2.6\",\n  \"_originalSource\": \"jquery-validation-unobtrusive\"\n}"
  },
  {
    "path": "samples/AspNetCoreSample/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js",
    "content": "/*!\n** Unobtrusive validation support library for jQuery and jQuery Validate\n** Copyright (C) Microsoft Corporation. All rights reserved.\n*/\n\n/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */\n/*global document: false, jQuery: false */\n\n(function ($) {\n    var $jQval = $.validator,\n        adapters,\n        data_validation = \"unobtrusiveValidation\";\n\n    function setValidationValues(options, ruleName, value) {\n        options.rules[ruleName] = value;\n        if (options.message) {\n            options.messages[ruleName] = options.message;\n        }\n    }\n\n    function splitAndTrim(value) {\n        return value.replace(/^\\s+|\\s+$/g, \"\").split(/\\s*,\\s*/g);\n    }\n\n    function escapeAttributeValue(value) {\n        // As mentioned on http://api.jquery.com/category/selectors/\n        return value.replace(/([!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^`{|}~])/g, \"\\\\$1\");\n    }\n\n    function getModelPrefix(fieldName) {\n        return fieldName.substr(0, fieldName.lastIndexOf(\".\") + 1);\n    }\n\n    function appendModelPrefix(value, prefix) {\n        if (value.indexOf(\"*.\") === 0) {\n            value = value.replace(\"*.\", prefix);\n        }\n        return value;\n    }\n\n    function onError(error, inputElement) {  // 'this' is the form element\n        var container = $(this).find(\"[data-valmsg-for='\" + escapeAttributeValue(inputElement[0].name) + \"']\"),\n            replaceAttrValue = container.attr(\"data-valmsg-replace\"),\n            replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null;\n\n        container.removeClass(\"field-validation-valid\").addClass(\"field-validation-error\");\n        error.data(\"unobtrusiveContainer\", container);\n\n        if (replace) {\n            container.empty();\n            error.removeClass(\"input-validation-error\").appendTo(container);\n        }\n        else {\n            error.hide();\n        }\n    }\n\n    function onErrors(event, validator) {  // 'this' is the form element\n        var container = $(this).find(\"[data-valmsg-summary=true]\"),\n            list = container.find(\"ul\");\n\n        if (list && list.length && validator.errorList.length) {\n            list.empty();\n            container.addClass(\"validation-summary-errors\").removeClass(\"validation-summary-valid\");\n\n            $.each(validator.errorList, function () {\n                $(\"<li />\").html(this.message).appendTo(list);\n            });\n        }\n    }\n\n    function onSuccess(error) {  // 'this' is the form element\n        var container = error.data(\"unobtrusiveContainer\");\n\n        if (container) {\n            var replaceAttrValue = container.attr(\"data-valmsg-replace\"),\n                replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null;\n\n            container.addClass(\"field-validation-valid\").removeClass(\"field-validation-error\");\n            error.removeData(\"unobtrusiveContainer\");\n\n            if (replace) {\n                container.empty();\n            }\n        }\n    }\n\n    function onReset(event) {  // 'this' is the form element\n        var $form = $(this),\n            key = '__jquery_unobtrusive_validation_form_reset';\n        if ($form.data(key)) {\n            return;\n        }\n        // Set a flag that indicates we're currently resetting the form.\n        $form.data(key, true);\n        try {\n            $form.data(\"validator\").resetForm();\n        } finally {\n            $form.removeData(key);\n        }\n\n        $form.find(\".validation-summary-errors\")\n            .addClass(\"validation-summary-valid\")\n            .removeClass(\"validation-summary-errors\");\n        $form.find(\".field-validation-error\")\n            .addClass(\"field-validation-valid\")\n            .removeClass(\"field-validation-error\")\n            .removeData(\"unobtrusiveContainer\")\n            .find(\">*\")  // If we were using valmsg-replace, get the underlying error\n                .removeData(\"unobtrusiveContainer\");\n    }\n\n    function validationInfo(form) {\n        var $form = $(form),\n            result = $form.data(data_validation),\n            onResetProxy = $.proxy(onReset, form),\n            defaultOptions = $jQval.unobtrusive.options || {},\n            execInContext = function (name, args) {\n                var func = defaultOptions[name];\n                func && $.isFunction(func) && func.apply(form, args);\n            }\n\n        if (!result) {\n            result = {\n                options: {  // options structure passed to jQuery Validate's validate() method\n                    errorClass: defaultOptions.errorClass || \"input-validation-error\",\n                    errorElement: defaultOptions.errorElement || \"span\",\n                    errorPlacement: function () {\n                        onError.apply(form, arguments);\n                        execInContext(\"errorPlacement\", arguments);\n                    },\n                    invalidHandler: function () {\n                        onErrors.apply(form, arguments);\n                        execInContext(\"invalidHandler\", arguments);\n                    },\n                    messages: {},\n                    rules: {},\n                    success: function () {\n                        onSuccess.apply(form, arguments);\n                        execInContext(\"success\", arguments);\n                    }\n                },\n                attachValidation: function () {\n                    $form\n                        .off(\"reset.\" + data_validation, onResetProxy)\n                        .on(\"reset.\" + data_validation, onResetProxy)\n                        .validate(this.options);\n                },\n                validate: function () {  // a validation function that is called by unobtrusive Ajax\n                    $form.validate();\n                    return $form.valid();\n                }\n            };\n            $form.data(data_validation, result);\n        }\n\n        return result;\n    }\n\n    $jQval.unobtrusive = {\n        adapters: [],\n\n        parseElement: function (element, skipAttach) {\n            /// <summary>\n            /// Parses a single HTML element for unobtrusive validation attributes.\n            /// </summary>\n            /// <param name=\"element\" domElement=\"true\">The HTML element to be parsed.</param>\n            /// <param name=\"skipAttach\" type=\"Boolean\">[Optional] true to skip attaching the\n            /// validation to the form. If parsing just this single element, you should specify true.\n            /// If parsing several elements, you should specify false, and manually attach the validation\n            /// to the form when you are finished. The default is false.</param>\n            var $element = $(element),\n                form = $element.parents(\"form\")[0],\n                valInfo, rules, messages;\n\n            if (!form) {  // Cannot do client-side validation without a form\n                return;\n            }\n\n            valInfo = validationInfo(form);\n            valInfo.options.rules[element.name] = rules = {};\n            valInfo.options.messages[element.name] = messages = {};\n\n            $.each(this.adapters, function () {\n                var prefix = \"data-val-\" + this.name,\n                    message = $element.attr(prefix),\n                    paramValues = {};\n\n                if (message !== undefined) {  // Compare against undefined, because an empty message is legal (and falsy)\n                    prefix += \"-\";\n\n                    $.each(this.params, function () {\n                        paramValues[this] = $element.attr(prefix + this);\n                    });\n\n                    this.adapt({\n                        element: element,\n                        form: form,\n                        message: message,\n                        params: paramValues,\n                        rules: rules,\n                        messages: messages\n                    });\n                }\n            });\n\n            $.extend(rules, { \"__dummy__\": true });\n\n            if (!skipAttach) {\n                valInfo.attachValidation();\n            }\n        },\n\n        parse: function (selector) {\n            /// <summary>\n            /// Parses all the HTML elements in the specified selector. It looks for input elements decorated\n            /// with the [data-val=true] attribute value and enables validation according to the data-val-*\n            /// attribute values.\n            /// </summary>\n            /// <param name=\"selector\" type=\"String\">Any valid jQuery selector.</param>\n\n            // $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one\n            // element with data-val=true\n            var $selector = $(selector),\n                $forms = $selector.parents()\n                                  .addBack()\n                                  .filter(\"form\")\n                                  .add($selector.find(\"form\"))\n                                  .has(\"[data-val=true]\");\n\n            $selector.find(\"[data-val=true]\").each(function () {\n                $jQval.unobtrusive.parseElement(this, true);\n            });\n\n            $forms.each(function () {\n                var info = validationInfo(this);\n                if (info) {\n                    info.attachValidation();\n                }\n            });\n        }\n    };\n\n    adapters = $jQval.unobtrusive.adapters;\n\n    adapters.add = function (adapterName, params, fn) {\n        /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation.</summary>\n        /// <param name=\"adapterName\" type=\"String\">The name of the adapter to be added. This matches the name used\n        /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>\n        /// <param name=\"params\" type=\"Array\" optional=\"true\">[Optional] An array of parameter names (strings) that will\n        /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and\n        /// mmmm is the parameter name).</param>\n        /// <param name=\"fn\" type=\"Function\">The function to call, which adapts the values from the HTML\n        /// attributes into jQuery Validate rules and/or messages.</param>\n        /// <returns type=\"jQuery.validator.unobtrusive.adapters\" />\n        if (!fn) {  // Called with no params, just a function\n            fn = params;\n            params = [];\n        }\n        this.push({ name: adapterName, params: params, adapt: fn });\n        return this;\n    };\n\n    adapters.addBool = function (adapterName, ruleName) {\n        /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where\n        /// the jQuery Validate validation rule has no parameter values.</summary>\n        /// <param name=\"adapterName\" type=\"String\">The name of the adapter to be added. This matches the name used\n        /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>\n        /// <param name=\"ruleName\" type=\"String\" optional=\"true\">[Optional] The name of the jQuery Validate rule. If not provided, the value\n        /// of adapterName will be used instead.</param>\n        /// <returns type=\"jQuery.validator.unobtrusive.adapters\" />\n        return this.add(adapterName, function (options) {\n            setValidationValues(options, ruleName || adapterName, true);\n        });\n    };\n\n    adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) {\n        /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where\n        /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and\n        /// one for min-and-max). The HTML parameters are expected to be named -min and -max.</summary>\n        /// <param name=\"adapterName\" type=\"String\">The name of the adapter to be added. This matches the name used\n        /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>\n        /// <param name=\"minRuleName\" type=\"String\">The name of the jQuery Validate rule to be used when you only\n        /// have a minimum value.</param>\n        /// <param name=\"maxRuleName\" type=\"String\">The name of the jQuery Validate rule to be used when you only\n        /// have a maximum value.</param>\n        /// <param name=\"minMaxRuleName\" type=\"String\">The name of the jQuery Validate rule to be used when you\n        /// have both a minimum and maximum value.</param>\n        /// <param name=\"minAttribute\" type=\"String\" optional=\"true\">[Optional] The name of the HTML attribute that\n        /// contains the minimum value. The default is \"min\".</param>\n        /// <param name=\"maxAttribute\" type=\"String\" optional=\"true\">[Optional] The name of the HTML attribute that\n        /// contains the maximum value. The default is \"max\".</param>\n        /// <returns type=\"jQuery.validator.unobtrusive.adapters\" />\n        return this.add(adapterName, [minAttribute || \"min\", maxAttribute || \"max\"], function (options) {\n            var min = options.params.min,\n                max = options.params.max;\n\n            if (min && max) {\n                setValidationValues(options, minMaxRuleName, [min, max]);\n            }\n            else if (min) {\n                setValidationValues(options, minRuleName, min);\n            }\n            else if (max) {\n                setValidationValues(options, maxRuleName, max);\n            }\n        });\n    };\n\n    adapters.addSingleVal = function (adapterName, attribute, ruleName) {\n        /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where\n        /// the jQuery Validate validation rule has a single value.</summary>\n        /// <param name=\"adapterName\" type=\"String\">The name of the adapter to be added. This matches the name used\n        /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name).</param>\n        /// <param name=\"attribute\" type=\"String\">[Optional] The name of the HTML attribute that contains the value.\n        /// The default is \"val\".</param>\n        /// <param name=\"ruleName\" type=\"String\" optional=\"true\">[Optional] The name of the jQuery Validate rule. If not provided, the value\n        /// of adapterName will be used instead.</param>\n        /// <returns type=\"jQuery.validator.unobtrusive.adapters\" />\n        return this.add(adapterName, [attribute || \"val\"], function (options) {\n            setValidationValues(options, ruleName || adapterName, options.params[attribute]);\n        });\n    };\n\n    $jQval.addMethod(\"__dummy__\", function (value, element, params) {\n        return true;\n    });\n\n    $jQval.addMethod(\"regex\", function (value, element, params) {\n        var match;\n        if (this.optional(element)) {\n            return true;\n        }\n\n        match = new RegExp(params).exec(value);\n        return (match && (match.index === 0) && (match[0].length === value.length));\n    });\n\n    $jQval.addMethod(\"nonalphamin\", function (value, element, nonalphamin) {\n        var match;\n        if (nonalphamin) {\n            match = value.match(/\\W/g);\n            match = match && match.length >= nonalphamin;\n        }\n        return match;\n    });\n\n    if ($jQval.methods.extension) {\n        adapters.addSingleVal(\"accept\", \"mimtype\");\n        adapters.addSingleVal(\"extension\", \"extension\");\n    } else {\n        // for backward compatibility, when the 'extension' validation method does not exist, such as with versions\n        // of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for\n        // validating the extension, and ignore mime-type validations as they are not supported.\n        adapters.addSingleVal(\"extension\", \"extension\", \"accept\");\n    }\n\n    adapters.addSingleVal(\"regex\", \"pattern\");\n    adapters.addBool(\"creditcard\").addBool(\"date\").addBool(\"digits\").addBool(\"email\").addBool(\"number\").addBool(\"url\");\n    adapters.addMinMax(\"length\", \"minlength\", \"maxlength\", \"rangelength\").addMinMax(\"range\", \"min\", \"max\", \"range\");\n    adapters.addMinMax(\"minlength\", \"minlength\").addMinMax(\"maxlength\", \"minlength\", \"maxlength\");\n    adapters.add(\"equalto\", [\"other\"], function (options) {\n        var prefix = getModelPrefix(options.element.name),\n            other = options.params.other,\n            fullOtherName = appendModelPrefix(other, prefix),\n            element = $(options.form).find(\":input\").filter(\"[name='\" + escapeAttributeValue(fullOtherName) + \"']\")[0];\n\n        setValidationValues(options, \"equalTo\", element);\n    });\n    adapters.add(\"required\", function (options) {\n        // jQuery Validate equates \"required\" with \"mandatory\" for checkbox elements\n        if (options.element.tagName.toUpperCase() !== \"INPUT\" || options.element.type.toUpperCase() !== \"CHECKBOX\") {\n            setValidationValues(options, \"required\", true);\n        }\n    });\n    adapters.add(\"remote\", [\"url\", \"type\", \"additionalfields\"], function (options) {\n        var value = {\n            url: options.params.url,\n            type: options.params.type || \"GET\",\n            data: {}\n        },\n            prefix = getModelPrefix(options.element.name);\n\n        $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) {\n            var paramName = appendModelPrefix(fieldName, prefix);\n            value.data[paramName] = function () {\n                var field = $(options.form).find(\":input\").filter(\"[name='\" + escapeAttributeValue(paramName) + \"']\");\n                // For checkboxes and radio buttons, only pick up values from checked fields.\n                if (field.is(\":checkbox\")) {\n                    return field.filter(\":checked\").val() || field.filter(\":hidden\").val() || '';\n                }\n                else if (field.is(\":radio\")) {\n                    return field.filter(\":checked\").val() || '';\n                }\n                return field.val();\n            };\n        });\n\n        setValidationValues(options, \"remote\", value);\n    });\n    adapters.add(\"password\", [\"min\", \"nonalphamin\", \"regex\"], function (options) {\n        if (options.params.min) {\n            setValidationValues(options, \"minlength\", options.params.min);\n        }\n        if (options.params.nonalphamin) {\n            setValidationValues(options, \"nonalphamin\", options.params.nonalphamin);\n        }\n        if (options.params.regex) {\n            setValidationValues(options, \"regex\", options.params.regex);\n        }\n    });\n\n    $(function () {\n        $jQval.unobtrusive.parse(document);\n    });\n}(jQuery));"
  },
  {
    "path": "samples/DotNetCoreSample/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n  <connectionStrings>\n    <add name=\"Test\" connectionString=\"server=.;database=Test;Integrated Security=True\" />\n    <add name=\"TestDb\" connectionString=\"server=.;database=TestDb;Integrated Security=True\" />\n  </connectionStrings>\n  <appSettings>\n    <add key=\"key1\" value=\"0\" />\n    <add key=\"key2\" value=\"1\" />\n    <add key=\"key3\" value=\"true\" />\n  </appSettings>\n</configuration>"
  },
  {
    "path": "samples/DotNetCoreSample/AppHostTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\nusing Microsoft.Extensions.Logging.Console;\nusing System.Net;\nusing System.Text.Encodings.Web;\nusing System.Text.Json;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Helpers.Hosting;\n\nnamespace DotNetCoreSample;\n\npublic static class AppHostTest\n{\n    public static async Task MainTest()\n    {\n        var builder = AppHost.CreateBuilder();\n        builder.Configuration.AddJsonFile(\"appsettings.json\");\n        builder.Logging.AddRelaxedJsonConsole(options =>\n        {\n            options.TimestampFormat = \"yyyy-MM-dd HH:mm:ss\";\n        });\n        // builder.AddHostedService<TimerService>();\n        // builder.AddHostedService<DiagnosticBackgroundService>();\n\n        builder.Services.AddSingleton<IWebServer, HttpListenerWebServer>();\n        builder.AddHostedService<WebServerHostedService>();\n        var cts = new CancellationTokenSource(10_000);\n        var app = builder.Build();\n        await app.RunAsync(cts.Token);\n    }\n}\n\nfile sealed class TimerService : TimerBaseBackgroundService\n{\n    protected override TimeSpan Period => TimeSpan.FromSeconds(1);\n\n    protected override Task ExecuteTaskAsync(CancellationToken stoppingToken)\n    {\n        Console.WriteLine(DateTimeOffset.Now);\n        return Task.CompletedTask;\n    }\n}\n\nfile sealed class DiagnosticBackgroundService(IServiceProvider serviceProvider) : CronBasedBackgroundServiceWithDiagnostic(serviceProvider)\n{\n    protected override string CronExpression => CronHelper.Secondly;\n\n    protected override Task ExecuteTaskInternalAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken)\n    {\n        Console.WriteLine(DateTimeOffset.Now);\n        return Task.CompletedTask;\n    }\n}\n\npublic static partial class LoggingBuilderExtensions\n{\n    public static ILoggingBuilder AddRelaxedJsonConsole(this ILoggingBuilder loggingBuilder,\n        Action<JsonConsoleFormatterOptions>? optionsConfigure = null)\n    {\n        return loggingBuilder.AddJsonConsole(options =>\n        {\n            options.JsonWriterOptions = new JsonWriterOptions()\n            {\n                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping\n            };\n            optionsConfigure?.Invoke(options);\n        });\n    }\n}\n\nfile interface IWebServer\n{\n    Task StartAsync(CancellationToken cancellationToken);\n\n    Task StopAsync(CancellationToken cancellationToken);\n}\n\nfile sealed class HttpListenerWebServer(IServiceProvider serviceProvider) : IWebServer\n{\n    private readonly IServiceProvider _serviceProvider = serviceProvider;\n    private readonly HttpListener _listener = new();\n\n    public async Task StartAsync(CancellationToken cancellationToken)\n    {\n        _listener.Prefixes.Add(\"http://localhost:5100/\");\n        _listener.Start();\n        var logger = _serviceProvider.GetRequiredService<ILogger<HttpListenerWebServer>>();\n        logger.LogInformation(\"WebServer started\");\n\n        while (!cancellationToken.IsCancellationRequested)\n        {\n            var listenerContext = await _listener.GetContextAsync();\n            try\n            {\n                await listenerContext.Response.OutputStream.WriteAsync(\"Hello World\"u8.ToArray(), cancellationToken);\n            }\n            catch (Exception) when (!cancellationToken.IsCancellationRequested)\n            {\n                throw;\n            }\n            finally\n            {\n                listenerContext.Response.Close();\n            }\n        }\n\n        _listener.Stop();\n    }\n\n    public Task StopAsync(CancellationToken cancellationToken)\n    {\n        _listener.Stop();\n        return Task.CompletedTask;\n    }\n}\n\nfile sealed class WebServerHostedService(IWebServer server) : BackgroundService\n{\n    private readonly IWebServer _server = server;\n\n    public override async Task StopAsync(CancellationToken cancellationToken)\n    {\n        await _server.StopAsync(cancellationToken);\n        await base.StopAsync(cancellationToken);\n    }\n\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        await _server.StartAsync(stoppingToken);\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/AspectTest.cs",
    "content": "﻿using Microsoft.EntityFrameworkCore;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Aspect;\nusing WeihanLi.Common.DependencyInjection;\nusing WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\npublic class TestDbContext(DbContextOptions<TestDbContext> dbContextOptions) : DbContext(dbContextOptions)\n{\n    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)\n    {\n        // optionsBuilder.UseSqlServer(DbConnectionString);\n        optionsBuilder.UseInMemoryDatabase(\"Tests\");\n    }\n\n    public DbSet<TestEntity> TestEntities { get; set; } = null!;\n}\n\ninternal class DbContextSaveInterceptor : IInterceptor\n{\n    public async Task Invoke(IInvocation invocation, Func<Task> next)\n    {\n        if (invocation.Target is DbContext dbContext)\n        {\n            dbContext.ChangeTracker.DetectChanges();\n            foreach (var entry in dbContext.ChangeTracker.Entries())\n            {\n                Console.WriteLine(\"---------------\");\n                Console.WriteLine(entry.Entity.ToJson());\n                Console.WriteLine(\"---------------\");\n            }\n        }\n        if (invocation.ProxyTarget is DbContext dbContext1)\n        {\n            dbContext1.ChangeTracker.DetectChanges();\n            foreach (var entry in dbContext1.ChangeTracker.Entries())\n            {\n                Console.WriteLine(\"---------------\");\n                Console.WriteLine(entry.Entity.ToJson());\n                Console.WriteLine(\"---------------\");\n            }\n        }\n        Console.WriteLine($\"{invocation.ProxyMethod.Name} before\");\n        await next();\n        Console.WriteLine($\"{invocation.ProxyMethod.Name} after\");\n    }\n}\n\ninternal class AspectTest\n{\n    public static void ServiceContainerTest()\n    {\n        var builder = new ServiceContainerBuilder();\n        builder.AddSingletonProxy<IFly, MonkeyKing>();\n\n        using var container = builder.BuildFluentAspectsContainer(options =>\n        {\n            options.InterceptAll()\n                .With<LogInterceptor>();\n        });\n\n        container.ResolveRequiredService<IFly>()\n            .Fly();\n\n        Console.WriteLine();\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/Base64UrlEncodeTest.cs",
    "content": "﻿using WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\npublic class Base64UrlEncodeTest\n{\n    private const string Jwt = \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxODI5NCIsIm5hbWUiOiJtdm4xNSIsInVuaXF1ZV9uYW1lIjoibXZuMTUiLCJuYmYiOjE1MzM1MzM1NzAsImV4cCI6MTUzNDM5NzU3MCwiaWF0IjoxNTMzNTMzNTcwLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjYzODI3LyIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NjM4MjcvIn0.Quf78Ma-T5cLsLPy7VngYpRzVYY8T4YkLJ1Mg_A3VmeBJY-wUUzmH7YdYn6c8zet1g9U5xwQ44bPylwcxI8es7Swsvp9AmQLwgLWCr8pmZgCVbzlrE4o9sDzE7F0CBkkev1UMav2vZ0Ksy32hRCR9hQlco4ieBMV2x4PDiR937p3mQKGQQPI_hSHCHO40J-ELXsQqmlOSmV2sYqffKmLkV1UrUEGFj8nF9Gcm-fJhZtj3yo0-KojQhX7j7ZahG8HnL4D88rWpcsL1TFJNvlcJqPDy0DxZLe0C8UoVJWyKg6j6wqT9L_zfIk_HAd-OTqr7cen-F5j1JA7CsSbT5aWgQ\";\n\n    public static void MainTest()\n    {\n        Console.WriteLine($\"Token:{Jwt}\");\n        Console.WriteLine(\"----------------------\");\n        Console.WriteLine(\"decode info:\");\n        foreach (var str in Jwt.Split('.'))\n        {\n            Console.WriteLine(str.Base64UrlDecode());\n        }\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/CommandExecutorTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the MIT license.\n\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\nusing static WeihanLi.Common.Helpers.CommandExecutor;\n\nnamespace DotNetCoreSample;\n\ninternal static class CommandExecutorTest\n{\n    public static void MainTest()\n    {\n        ExecuteAndCapture(\"hostname\")\n            .PrintOutputToConsole()\n            .EnsureSuccessExitCode();\n\n        ExecuteAndOutput(\"hostname\").EnsureSuccessExitCode();\n\n        ExecuteAndOutputAsync(\"hostname\").Wait();\n\n        ExecuteCommandAndOutput(\"hostname\").EnsureSuccessExitCode();\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/CronHelperTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\npublic static class CronHelperTest\n{\n    public static void MainTest()\n    {\n        // http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html\n        var nextTickUtc = CronHelper.GetNextOccurrence(\"0 15 10 * * ?\");\n        var nextTickLocal = CronHelper.GetNextOccurrence(\"0 15 10 * * ?\", TimeZoneInfo.Local);\n\n        Console.WriteLine($\"@utc next tick: {nextTickUtc.GetValueOrDefault().DateTime.ToTimeString()}  {Environment.NewLine} local next tick:{nextTickLocal.GetValueOrDefault().DateTime.ToTimeString()}\");\n\n        var nextTicks = CronHelper.GetNextOccurrences(\"0 15 10-20 * * ?\", TimeSpan.FromHours(6), TimeZoneInfo.Local).Take(5).ToArray();\n        foreach (var tick in nextTicks)\n        {\n            Console.WriteLine(tick.DateTime.ToTimeString());\n        }\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/DataExtensionTest.cs",
    "content": "﻿using Microsoft.Data.SqlClient;\nusing Microsoft.Extensions.Configuration;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Data.Common;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Logging;\nusing WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\npublic class DataExtensionTest\n{\n    /// <summary>\n    /// Logger\n    /// </summary>\n    private static readonly ILogHelperLogger Logger = LogHelper.GetLogger<DataExtensionTest>();\n\n    private static void Init(DbConnection conn)\n    {\n        var isTableExist = conn.ExecuteScalarToOrDefault<bool>(@\"SELECT 1\n            FROM dbo.sysobjects\n            WHERE id = OBJECT_ID(N'[dbo].[TestTable111]')\n            AND OBJECTPROPERTY(id, N'IsUserTable') = 1;\");\n        if (!isTableExist)\n        {\n            conn.Execute(@\"CREATE TABLE [dbo].[TestTable111](\n                    [PKID] [INT] IDENTITY(1, 1) PRIMARY KEY NOT NULL,\n\n                    [Token] [NVARCHAR](200) NULL,\n\n                    [CreatedTime] [DATETIME] NULL)\");\n        }\n    }\n\n    public static void MainTest()\n    {\n        var connString = DependencyResolver.ResolveRequiredService<IConfiguration>().GetConnectionString(\"TestDb\");\n        using var conn = new SqlConnection(connString);\n        Init(conn);\n\n        for (var i = 0; i < 3; i++)\n        {\n            conn.Execute(@\"INSERT INTO [dbo].[TestTable111]\n                            (\n                            [Token],\n                        [CreatedTime]\n                        )\n                    VALUES\n                        (@Token, --Token - nvarchar(200)\n                    @CreatedTime-- CreatedTime - datetime\n                        )\", new TestEntity\n            {\n                Token = Guid.NewGuid().ToString(\"N\") + \"_Execute_Model\",\n                CreatedTime = DateTime.Now\n            });\n        }\n\n        Console.WriteLine(\"Current data count:{0}\", conn.ExecuteScalarTo<int>(\"SELECT COUNT(1) FROM [dbo].[TestTable111]\"));\n\n        for (var i = 0; i < 3; i++)\n        {\n            conn.Execute(@\"INSERT INTO [dbo].[TestTable111]\n                            (\n                            [Token],\n                        [CreatedTime]\n                        )\n                    VALUES\n                        (@Token, --Token - nvarchar(200)\n                    GETDATE()-- CreatedTime - datetime\n                        )\", new TestEntity\n            {\n                Token = Guid.NewGuid().ToString(\"N\") + \"_Execute_Model_1\"\n            });\n        }\n\n        Console.WriteLine(\"Current data count:{0}\", conn.ExecuteScalarTo<int>(\"SELECT COUNT(1) FROM [dbo].[TestTable111]\"));\n\n        for (var i = 0; i < 3; i++)\n        {\n            conn.Execute(@\"INSERT INTO [dbo].[TestTable111]\n                            (\n                            [Token],\n                        [CreatedTime]\n                        )\n                    VALUES\n                        (@Token, --Token - nvarchar(200)\n                    GETDATE()-- CreatedTime - datetime\n                        )\", new\n            {\n                Token = Guid.NewGuid().ToString(\"N\") + \"_Execute_Anonymous_Model\"\n            });\n        }\n\n        Console.WriteLine(\"Current data count:{0}\", conn.ExecuteScalarTo<int>(\"SELECT COUNT(1) FROM [dbo].[TestTable111]\"));\n\n        var tokens = conn.QueryColumn<string>(\"SELECT Token FROM [dbo].[TestTable111]\");\n        Console.WriteLine(\"tokens:{0}\", string.Join(\",\", tokens));\n\n        var ids = conn.Select<int>(\"SELECT PKID FROM [dbo].[TestTable111]\");\n        Console.WriteLine(\"ids:{0}\", string.Join(\",\", ids));\n\n        var lastId = conn.Fetch<int>(\"SELECT TOP 1 PKID FROM [dbo].[TestTable111] ORDER BY PKID DESC\");\n        Console.WriteLine(\"lastId:{0}\", lastId);\n\n        conn.Execute(\"Delete from TestTable111 where PKID > @pkid\", new { pkid = 888 });\n\n        Console.WriteLine(\"Current data count:{0}\", conn.ExecuteScalarTo<int>(\"SELECT COUNT(1) FROM [dbo].[TestTable111]\"));\n\n        Console.WriteLine(conn.Fetch<TestEntity>(\"select top 1 * from TestTable111\")?.Token);\n\n        foreach (var entity in conn.Select<TestEntity>(\"select * from TestTable111\"))\n        {\n            Console.WriteLine(entity.Token);\n        }\n\n        Clean(conn);\n    }\n\n    private static void Clean(DbConnection conn)\n    {\n        conn.Execute(\"DROP TABLE [dbo].[TestTable111]\");\n    }\n}\n\n[Table(\"tabTestEntity\")]\npublic class TestEntity\n{\n    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]\n    [Column(\"PKID\")]\n    public int Id { get; set; }\n\n    public string? Token { get; set; }\n\n    public DateTime CreatedTime { get; set; }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/DependencyInjectionTest.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing System.Diagnostics;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Aspect;\nusing WeihanLi.Common.DependencyInjection;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Logging;\nusing WeihanLi.Common.Services;\nusing WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\ninternal class DependencyInjectionTest\n{\n    private static readonly ILogHelperLogger Logger = LogHelper.GetLogger<DependencyInjectionTest>();\n\n    public static void Test()\n    {\n        var fly = DependencyResolver.ResolveRequiredService<IFly>();\n        Console.WriteLine(fly.Name);\n        fly.Fly();\n\n        DependencyResolver.TryInvoke<IFly>(f =>\n        {\n            Console.WriteLine(f.Name);\n            f.Fly();\n        });\n    }\n\n    public static void BuiltInIocTest()\n    {\n        IServiceContainerBuilder containerBuilder = new ServiceContainerBuilder();\n        containerBuilder.AddSingleton<IConfiguration>(new ConfigurationBuilder()\n            .AddJsonFile(\"appsettings.json\")\n            .Build()\n        );\n        containerBuilder.AddScoped<IFly, MonkeyKing>();\n        containerBuilder.AddScoped<IFly, Superman>();\n\n        containerBuilder.AddScoped<HasDependencyTest>();\n        containerBuilder.AddScoped<HasDependencyTest1>();\n        containerBuilder.AddScoped<HasDependencyTest2>();\n        containerBuilder.AddScoped<HasDependencyTest3>();\n        containerBuilder.AddScoped(typeof(HasDependencyTest4<>));\n\n        containerBuilder.AddTransient<WuKong>();\n        containerBuilder.AddScoped<WuJing>(serviceProvider => new WuJing());\n        containerBuilder.AddSingleton(typeof(GenericServiceTest<>));\n\n        containerBuilder.RegisterAssemblyModules();\n\n        using (var container = containerBuilder.Build())\n        {\n            var idGenerator = container.ResolveRequiredService<IIdGenerator>();\n            Console.WriteLine(idGenerator.NewId());\n            var rootConfig = container.ResolveRequiredService<IConfiguration>();\n            //try\n            //{\n            //    container.ResolveRequiredService<IFly>();\n            //}\n            //catch (Exception e)\n            //{\n            //    Console.WriteLine(e);\n            //}\n\n            using (var scope = container.CreateScope())\n            {\n                var config = scope.ResolveRequiredService<IConfiguration>();\n                var fly1 = scope.ResolveRequiredService<IFly>();\n                var fly2 = scope.ResolveRequiredService<IFly>();\n\n                var wukong1 = scope.ResolveRequiredService<WuKong>();\n                var wukong2 = scope.ResolveRequiredService<WuKong>();\n\n                var wuJing1 = scope.ResolveRequiredService<WuJing>();\n                var wuJing2 = scope.ResolveRequiredService<WuJing>();\n\n                Console.WriteLine(\"fly1 == fly2,  {0}\", fly1 == fly2);\n                Console.WriteLine(\"rootConfig == config, {0}\", rootConfig == config);\n                Console.WriteLine(\"wukong1 == wukong2, {0}\", wukong1 == wukong2);\n                Console.WriteLine(\"wujing1 == wujing2, {0}\", wuJing1 == wuJing2);\n\n                fly1.Fly();\n\n                wukong1.Jump();\n                wukong2.Jump();\n\n                wuJing1.Eat();\n\n                var s0 = scope.ResolveRequiredService<HasDependencyTest>();\n                s0.Test();\n                Console.WriteLine($\"s0._fly == fly1 : {s0._fly == fly1}\");\n\n                var s1 = scope.ResolveRequiredService<HasDependencyTest1>();\n                s1.Test();\n\n                var s2 = scope.ResolveRequiredService<HasDependencyTest2>();\n                s2.Test();\n\n                var s3 = scope.ResolveRequiredService<HasDependencyTest3>();\n                s3.Test();\n\n                var s4 = scope.ResolveRequiredService<HasDependencyTest4<string>>();\n                s4.Test();\n\n                using (var innerScope = scope.CreateScope())\n                {\n                    var config2 = innerScope.ResolveRequiredService<IConfiguration>();\n                    Console.WriteLine(\"rootConfig == config2, {0}\", rootConfig == config2);\n                    var fly3 = innerScope.ResolveRequiredService<IFly>();\n                    fly3.Fly();\n                }\n\n                var number = config.GetAppSetting<int>(\"Number\");\n                Console.WriteLine(number);\n\n                var flySvcs = scope.ResolveServices<IFly>();\n                foreach (var f in flySvcs)\n                    f.Fly();\n            }\n\n            var genericService1 = container.ResolveRequiredService<GenericServiceTest<int>>();\n            genericService1.Test();\n\n            var genericService2 = container.ResolveRequiredService<GenericServiceTest<string>>();\n            genericService2.Test();\n        }\n    }\n\n    private class WuKong : IDisposable\n    {\n        public WuKong()\n        {\n            Console.WriteLine(\"wukong initialized\");\n        }\n\n        public void Jump()\n        {\n            Console.WriteLine(\"wukong jumped 一万八千里\");\n        }\n\n        public void Dispose()\n        {\n            Console.WriteLine(\"wukong disposed\");\n        }\n    }\n\n    private class WuJing : IDisposable\n    {\n        public WuJing()\n        {\n            Console.WriteLine(\"WuJing initialized\");\n        }\n\n        public void Eat()\n        {\n            Console.WriteLine(\"WuJing eated balaba.....\");\n        }\n\n        public void Dispose()\n        {\n            Console.WriteLine(\"WuJing disposed\");\n        }\n    }\n\n    private class GenericServiceTest<T>\n    {\n        public void Test()\n        {\n            Console.WriteLine($\"generic type: {typeof(T).FullName}\");\n        }\n    }\n\n    private class HasDependencyTest\n    {\n        public readonly IFly _fly;\n\n        public HasDependencyTest(IFly fly)\n        {\n            _fly = fly;\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {nameof(HasDependencyTest)}\");\n            _fly.Fly();\n        }\n    }\n\n    private class HasDependencyTest1\n    {\n        private readonly IReadOnlyCollection<IFly> _flys;\n\n        public HasDependencyTest1(IEnumerable<IFly> flys)\n        {\n            _flys = flys.ToArray();\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {nameof(HasDependencyTest1)}\");\n            foreach (var item in _flys)\n            {\n                item.Fly();\n            }\n        }\n    }\n\n    private class HasDependencyTest2\n    {\n        private readonly IReadOnlyCollection<IFly> _flys;\n\n        public HasDependencyTest2(IReadOnlyCollection<IFly> flys)\n        {\n            _flys = flys;\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {nameof(HasDependencyTest2)}\");\n            foreach (var item in _flys)\n            {\n                item.Fly();\n            }\n        }\n    }\n\n    private class HasDependencyTest3\n    {\n        private readonly IReadOnlyCollection<GenericServiceTest<int>> _svcs;\n\n        public HasDependencyTest3(IEnumerable<GenericServiceTest<int>> svcs)\n        {\n            _svcs = svcs.ToArray();\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {nameof(HasDependencyTest3)}\");\n            foreach (var item in _svcs)\n            {\n                item.Test();\n            }\n        }\n    }\n\n    private class HasDependencyTest4<T>\n    {\n        private readonly IReadOnlyCollection<GenericServiceTest<T>> _svcs;\n\n        public HasDependencyTest4(IEnumerable<GenericServiceTest<T>> svcs)\n        {\n            _svcs = svcs.ToArray();\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {GetType().FullName}\");\n            foreach (var item in _svcs)\n            {\n                item.Test();\n            }\n        }\n    }\n}\n\npublic interface IFly\n{\n    string Name { get; }\n\n    void Fly();\n\n    void OpenFly<T>();\n\n    string FlyAway();\n}\n\npublic interface IAnimal<T>\n{\n    void Eat();\n}\n\npublic class Animal<T>\n{\n    private int _eatCounter;\n    private int _drinkCounter;\n\n    public virtual void Eat()\n    {\n        Console.WriteLine(\"Eating now\");\n        _eatCounter++;\n    }\n\n    public int GetEatCount() => _eatCounter;\n\n    public virtual void Drink(T t)\n    {\n        Console.WriteLine($\"ddd {t}\");\n        _drinkCounter++;\n    }\n\n    public virtual int GetDrinkCount() => _drinkCounter;\n}\n\npublic class LogInterceptor : AbstractInterceptor\n{\n    public override async Task Invoke(IInvocation invocation, Func<Task> next)\n    {\n        var watch = Stopwatch.StartNew();\n        try\n        {\n            Console.WriteLine($\"[{invocation.ProxyMethod.Name}] invoke begin\");\n            Console.WriteLine($\"Arguments:{invocation.Arguments?.ToJson()}\");\n            //Console.WriteLine($\"Properties:{invocation.Properties.ToJson()}\");\n\n            await next();\n        }\n        finally\n        {\n            watch.Stop();\n            Console.WriteLine($\"[{invocation.ProxyMethod.Name}] invoke complete, elasped:{watch.ElapsedMilliseconds} ms\");\n        }\n    }\n}\n\npublic class MonkeyKing : IFly, IDisposable\n{\n    public string Name => \"MonkeyKing\";\n\n    public void Fly()\n    {\n        Console.WriteLine($\"{Name} is flying\");\n    }\n\n    public void OpenFly<T>()\n    {\n        Console.WriteLine($\"{Name} is OpenFlying,OpenType: {typeof(T).FullName}\");\n    }\n\n    public string FlyAway() => \"十万八千里\";\n\n    public void Dispose()\n    {\n        Console.WriteLine($\"{Name}  disposed...\");\n    }\n}\n\ninternal class Superman : IFly\n{\n    public string Name => \"Superman\";\n\n    public void Fly()\n    {\n        Console.WriteLine(\"Superman is flying\");\n    }\n\n    public void OpenFly<T>()\n    {\n        Console.WriteLine($\"{Name} is OpenFlying,OpenType: {typeof(T).FullName}\");\n    }\n\n    public string FlyAway() => \"xxxxx\";\n}\n\ninternal class TestServiceContainerModule : ServiceContainerModule\n{\n    public override void ConfigureServices(IServiceContainerBuilder serviceContainerBuilder)\n    {\n        serviceContainerBuilder.AddSingleton<IIdGenerator>(GuidIdGenerator.Instance);\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/DisposeTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Helpers;\n\nnamespace DotNetCoreSample;\n\npublic class DisposeTest\n{\n    public static void MainTest()\n    {\n        Console.WriteLine(@$\"---- {nameof(MainTest)} start\");\n        {\n            using var service = new TestService()\n            {\n                Name = \"MainTest\"\n            };\n        }\n        {\n            var service = new TestService()\n            {\n                Name = \"MainTest1\"\n            };\n            service.Dispose();\n        }\n        {\n            // forget to dispose\n            var service = new TestService()\n            {\n                Name = \"MainTest2\"\n            };\n            Console.WriteLine(service.GetType());\n\n            service = null;\n            Console.WriteLine(service is null);\n        }\n        GC.Collect();\n        GC.WaitForPendingFinalizers();\n\n        Console.WriteLine(@$\"---- {nameof(MainTest)} end\");\n    }\n\n    public static async ValueTask MainTestAsync()\n    {\n        Console.WriteLine(@$\"---- {nameof(MainTestAsync)} start\");\n        {\n            await using var service = new TestService()\n            {\n                Name = \"MainTestAsync\"\n            };\n        }\n        {\n            var service = new TestService()\n            {\n                Name = \"MainTestAsync1\"\n            };\n            await service.DisposeAsync();\n        }\n        {\n            // forget to dispose\n            var service = new TestService()\n            {\n                Name = \"MainTestAsync2\"\n            };\n            Console.WriteLine(service.GetType());\n\n            service = null;\n            Console.WriteLine(service is null);\n        }\n        GC.Collect();\n        GC.WaitForPendingFinalizers();\n\n        Console.WriteLine(@$\"---- {nameof(MainTestAsync)} end\");\n    }\n}\n\nfile sealed class TestService : DisposableBase\n{\n    public required string Name { get; init; }\n\n    protected override void Dispose(bool disposing)\n    {\n        if (disposing)\n        {\n            Console.WriteLine($@\"<<{Name}>> disposes managed resources\");\n        }\n        Console.WriteLine($@\"<<{Name}>> disposes unmanaged resources\");\n        base.Dispose(disposing);\n    }\n\n    protected override async ValueTask DisposeAsyncCore()\n    {\n        {\n            Console.WriteLine($@\"<<{Name}>> disposes managed resources async\");\n            await Task.Yield();\n        }\n        await base.DisposeAsyncCore();\n    }\n\n    ~TestService()\n    {\n        Console.WriteLine($@\"<<{Name}>> Finalizer executing\");\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/DotNetCoreSample.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>$(LatestTargetFramework)</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.EntityFrameworkCore\" />\n    <PackageReference Include=\"Microsoft.EntityFrameworkCore.InMemory\" />\n    <PackageReference Include=\"Microsoft.Extensions.ObjectPool\" />\n    <PackageReference Include=\"Serilog.Sinks.Console\" />\n    <PackageReference Include=\"Microsoft.Data.SqlClient\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\WeihanLi.Common.Logging.Serilog\\WeihanLi.Common.Logging.Serilog.csproj\" />\n    <ProjectReference Include=\"..\\..\\src\\WeihanLi.Common\\WeihanLi.Common.csproj\" />\n    <ProjectReference Include=\"..\\..\\src\\WeihanLi.Extensions.Hosting\\WeihanLi.Extensions.Hosting.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Update=\"appsettings.json\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "samples/DotNetCoreSample/EventTest.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\nusing System.Text.Json;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Event;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Logging;\nusing WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\ninternal class EventTest\n{\n    public static async Task MainTest()\n    {\n        {\n            Console.WriteLine(@\"EventBus dependency injection sample\");\n            var services = new ServiceCollection();\n            services.AddEvents()\n                .RegisterEventHandlers()\n                .AddEventHandler(new DelegateEventHandler<CounterEvent>(e => Console.WriteLine(@$\"Delegate event handler handling: {e.ToJson()}\")))\n                ;\n            await using var sp = services.BuildServiceProvider();\n            var eventBus = sp.GetRequiredService<IEventBus>();\n            await eventBus.PublishAsync(new CounterEvent { Counter = 1 });\n            await eventBus.PublishAsync(new CounterEvent { Counter = 2 });\n            await eventBus.PublishAsync(new CounterEvent() { Counter = 3 }, new EventProperties()\n            {\n                TraceId = Guid.NewGuid().ToString()\n            });\n        }\n\n        {\n            // none dependencyInjection sample\n            Console.WriteLine(@\"EventBus sample without dependency injection\");\n            var eventBus = new EventBus();\n            await eventBus.SubscribeAsync<CounterEvent, CounterEventHandler1>();\n            await eventBus.SubscribeAsync<CounterEvent, CounterEventHandler2>();\n            var delegateEventHandler = new DelegateEventHandler<CounterEvent>(e =>\n                Console.WriteLine(@$\"Delegate event handler handling: {e.ToJson()}\"));\n            await eventBus.SubscribeAsync(delegateEventHandler);\n\n            await eventBus.PublishAsync(new CounterEvent() { Counter = 1 });\n            await eventBus.PublishAsync(new CounterEvent() { Counter = 2 });\n\n            await eventBus.PublishAsync(new CounterEvent() { Counter = 3 }, new EventProperties()\n            {\n                TraceId = Guid.NewGuid().ToString()\n            });\n        }\n    }\n\n    public static async Task AckQueueTest()\n    {\n        var ackQueue = new AckQueue(new AckQueueOptions()\n        {\n            AckTimeout = TimeSpan.FromSeconds(1)\n        });\n        await ackQueue.EnqueueAsync(new CounterEvent { Counter = 1 });\n        await ackQueue.EnqueueAsync(new CounterEvent { Counter = 2 });\n\n        var event1 = await ackQueue.DequeueAsync<CounterEvent>();\n        ArgumentNullException.ThrowIfNull(event1);\n        Console.WriteLine(@$\"event1: {event1.ToJson()}\");\n\n        var event2 = await ackQueue.DequeueAsync<CounterEvent>();\n        ArgumentNullException.ThrowIfNull(event2);\n        Console.WriteLine(@$\"event2: {event2.ToJson()}\");\n\n        await ackQueue.AckMessageAsync(event2.Properties.EventId);\n        var event3 = await ackQueue.DequeueAsync<CounterEvent>();\n        Console.WriteLine(@$\"event3: {event3.ToJson()}\");\n\n        await Task.Delay(2000);\n        ackQueue.RequeueUnAckedMessages();\n\n        var event4 = await ackQueue.DequeueAsync<CounterEvent>();\n        Console.WriteLine(@$\"event4: {event4.ToJson()}\");\n        ArgumentNullException.ThrowIfNull(event4);\n        await ackQueue.AckMessageAsync(event4.Properties.EventId);\n    }\n}\n\npublic class CounterEvent\n{\n    public int Counter { get; set; }\n}\n\ninternal class CounterEventHandler1 : EventHandlerBase<CounterEvent>\n{\n    public override Task Handle(CounterEvent @event, EventProperties eventProperties)\n    {\n        Console.WriteLine($\"Event Info: {@event.ToJson()}, Handler Type:{GetType().FullName}, eventProperties: {JsonSerializer.Serialize(eventProperties)}\");\n        return Task.CompletedTask;\n    }\n}\n\ninternal class CounterEventHandler2 : EventHandlerBase<CounterEvent>\n{\n    public override Task Handle(CounterEvent @event, EventProperties eventProperties)\n    {\n        Console.WriteLine($\"Event Info: {@event.ToJson()}, Handler Type:{GetType().FullName}, eventProperties: {eventProperties.ToJson()}\");\n        return Task.CompletedTask;\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/GroupByEqualitySample.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\npublic static class GroupByEqualitySample\n{\n    public static void MainTest()\n    {\n        var students = new StudentResult[]\n        {\n            new() { StudentName = \"Ming\", CourseName = \"Chinese\", Score = 80, },\n            new()\n            {\n                StudentId = 1, StudentName = \"Ming\", CourseName = \"English\", Score = 60,\n            },\n            new()\n            {\n                StudentId = 2, StudentName = \"Mike\", CourseName = \"English\", Score = 70,\n            },\n            new() { StudentId = 1, CourseName = \"Math\", Score = 100, },\n            new()\n            {\n                StudentName = \"Mike\", CourseName = \"Chinese\", Score = 60,\n            },\n        };\n        var groups = students.GroupByEquality(x => new Student() { Id = x.StudentId, Name = x.StudentName },\n            (s1, s2) => s1.Id == s2.Id || s1.Name == s2.Name, (k, x) =>\n            {\n                if (k.Id <= 0 && x.StudentId > 0)\n                {\n                    k.Id = x.StudentId;\n                }\n                if (k.Name.IsNullOrEmpty() && x.StudentName.IsNotNullOrEmpty())\n                {\n                    k.Name = x.StudentName;\n                }\n            }, (x, k) =>\n            {\n                if (k.Id > 0 && x.StudentId <= 0)\n                {\n                    x.StudentId = k.Id;\n                }\n                if (k.Name.IsNotNullOrEmpty() && x.StudentName.IsNullOrEmpty())\n                {\n                    x.StudentName = k.Name;\n                }\n            });\n        foreach (var group in groups)\n        {\n            Console.WriteLine(\"-------------------------------------\");\n            Console.WriteLine($\"{group.Key.Id} {group.Key.Name}, Total score: {group.Sum(x => x.Score)}\");\n            foreach (var result in group)\n            {\n                Console.WriteLine($\"{result.StudentId}  {result.StudentName}\\n{result.CourseName}  {result.Score}\");\n            }\n        }\n    }\n\n\n\n    private sealed class Student\n    {\n        public int Id { get; set; }\n        public string Name { get; set; } = \"Ming\";\n    }\n\n    private sealed class StudentResult\n    {\n        public int StudentId { get; set; }\n        public string StudentName { get; set; } = string.Empty;\n\n        public string CourseName { get; set; } = string.Empty;\n        public int Score { get; set; }\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/HttpRequesterTest.cs",
    "content": "﻿using WeihanLi.Common.Http;\nusing WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\ninternal class HttpRequesterTest\n{\n    public static void MainTest()\n    {\n        var result = new WebRequestHttpRequester(\"https://weihanli.xyz\")\n            .WithReferer(\"https://weihanli.xyz\")\n            .WithHeaders(new Dictionary<string, string?>\n            {\n                    {\"Header1\", \"Header1\" }\n            })\n            .Execute();\n        System.Console.WriteLine(result);\n\n        result = new HttpClientHttpRequester()\n            .WithUrl(\"https://weihanli.xyz\")\n            .WithMethod(HttpMethod.Get)\n            .WithReferer(\"https://weihanli.xyz\")\n            .WithHeaders(new Dictionary<string, string?>\n            {\n                    {\"Header1\", \"Header1\" }\n            })\n            .Execute();\n        System.Console.WriteLine(result);\n\n        var loginResult = new WebRequestHttpRequester(\"https://accounting.weihanli.xyz/Account/LogOn\", HttpMethod.Post)\n           .WithHeaders(new Dictionary<string, string?>()\n           {\n                   // { \"X-Requested-With\", \"XMLHttpRequest\" },\n                   {\"Header1\", \"Header1\" }\n           })\n           .AjaxRequest()\n           .WithReferer(\"https://accounting.weihanli.xyz/Account/Login?ReturnUrl=%2F\")\n           .WithUserAgent(\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36\")\n           .WithFormParams(new Dictionary<string, string>()\n           {\n                   {\"Username\",\"liweihan\" },\n                   {\"Password\", \"112233\" },\n                   {\"RememberMe\",\"false\" }\n           })\n           .ExecuteForJson<WeihanLi.Common.Models.Result<bool>>();\n        System.Console.WriteLine(loginResult.ToJson());\n\n        var uploadFileResponse = new WebRequestHttpRequester(\"https://graph.baidu.com/upload\", HttpMethod.Post)\n            .WithFile($@\"{System.Environment.GetEnvironmentVariable(\"USERPROFILE\")}\\Pictures\\4e6ab53e383863ed4d15252039f70423.jpg\", \"image\", new Dictionary<string, string>()\n            {\n                    { \"tn\",\"pc\" },\n                    { \"from\",\"pc\" },\n                    { \"image_source\",\"PC_UPLOAD_SEARCH_FILE\" },\n                    { \"range\",\"{\\\"page_from\\\": \\\"searchIndex\\\"}\" },\n            })\n            .WithReferer(\"https://baidu.com/\")\n            .WithUserAgent(\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36\")\n            .ExecuteForResponse();\n        System.Console.WriteLine($\"Response status:{uploadFileResponse.StatusCode}, result:{uploadFileResponse.ResponseBytes.GetString()}\");\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/InMemoryStreamTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\ninternal static class InMemoryStreamTest\n{\n    public static async Task MainTest()\n    {\n        var stream = new InMemoryStream<long>(\"test\");\n        var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();\n        await Task.Delay(100);\n        await stream.AddAsync(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), new Dictionary<string, string>\n        {\n            { \"messages\", new { name = $\"test-{DateTimeOffset.Now}\" } .ToJson() }\n        });\n        Console.WriteLine(\"stream message added\");\n        await Task.Delay(1000);\n        await stream.AddAsync(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), new Dictionary<string, string>\n        {\n            { \"messages\", new { name = $\"test-{DateTimeOffset.Now}\" } .ToJson() }\n        });\n        Console.WriteLine(\"stream message added\");\n        //\n        {\n            Console.WriteLine(\"Fetch messages from stream\");\n            await foreach (var item in stream.FetchAsync(timestamp, 2))\n            {\n                Console.WriteLine($\"{item.Id} - {item.Timestamp}\");\n                Console.WriteLine(item.Fields.ToJson());\n            }\n        }\n        {\n            Console.WriteLine(\"Fetch messages from stream again\");\n            await foreach (var item in stream.FetchAsync(timestamp, 2))\n            {\n                Console.WriteLine($\"{item.Id} - {item.Timestamp}\");\n                Console.WriteLine(item.Fields.ToJson());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/LoggerTest.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Logging;\n\nnamespace DotNetCoreSample;\n\ninternal class LoggerTest\n{\n    public static void MainTest()\n    {\n        LogHelper.ConfigureLogging(builder =>\n        {\n            builder\n                .AddConsole()\n                //.AddSerilog(loggerConfig => loggerConfig.WriteTo.Console())\n                //.WithMinimumLevel(LogHelperLogLevel.Info)\n                //.WithFilter((category, level) => level > LogHelperLogLevel.Error && category.StartsWith(\"System\"))\n                //.EnrichWithProperty(\"Entry0\", ApplicationHelper.ApplicationName)\n                //.EnrichWithProperty(\"Entry1\", ApplicationHelper.ApplicationName, e => e.LogLevel >= LogHelperLogLevel.Error)\n                ;\n        });\n\n        var abc = \"1233\";\n        var logger = LogHelper.GetLogger<LoggerTest>();\n        logger.Debug(\"12333 {abc}\", abc);\n        logger.Trace(\"122334334\");\n        logger.Info($\"122334334 {abc}\");\n\n        logger.Warn(\"12333, err:{err}\", \"hahaha\");\n        logger.Error(\"122334334\");\n        logger.Fatal(\"12333\");\n    }\n\n    public static void MicrosoftLoggingTest()\n    {\n        var services = new ServiceCollection()\n            .AddLogging(builder =>\n                // builder.AddConsole()\n                builder.AddFile(options => options.LogFormatter = (category, level, exception, msg, timestamp) =>\n                    $\"{timestamp} - [{category}] {level} - {msg}\\n{exception}\")\n                )\n            .AddSingleton(typeof(GenericTest<>))\n            .BuildServiceProvider();\n\n        var logger = services.GetRequiredService<ILoggerFactory>()\n            .CreateLogger(\"test\");\n        while (!ApplicationHelper.ExitToken.IsCancellationRequested)\n        {\n            logger.LogInformation(\"Echo time: {Time}\", DateTimeOffset.Now);\n            Thread.Sleep(500);\n        }\n\n        ConsoleHelper.ReadKeyWithPrompt();\n        services.GetRequiredService<ILoggerFactory>()\n            .CreateLogger(\"test\")\n            .LogInformation(\"test 123\");\n        services.GetRequiredService<GenericTest<int>>()\n            .Test();\n        services.GetRequiredService<GenericTest<string>>()\n            .Test();\n\n        Console.WriteLine();\n\n        services = new ServiceCollection()\n            .AddLogging(builder => builder.AddConsole().UseCustomGenericLogger())\n            .AddSingleton(typeof(GenericTest<>))\n            .BuildServiceProvider();\n        services.GetRequiredService<GenericTest<int>>()\n            .Test();\n        services.GetRequiredService<GenericTest<string>>()\n            .Test();\n    }\n\n    private class GenericTest<T>(ILogger<GenericTest<T>> logger)\n    {\n        public void Test() => logger.LogInformation(\"test\");\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/MapperTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\n\nnamespace DotNetCoreSample;\n\ninternal class MapperTest\n{\n    private class MapperA\n    {\n        public string? Name { get; set; }\n\n        public int Age { get; set; }\n\n        public decimal Money { get; set; }\n    }\n\n    private class MapperB\n    {\n        public string? Name { get; set; }\n\n        public int Age { get; set; }\n    }\n\n    public static void Test()\n    {\n        var mapperA = new MapperA\n        {\n            Age = 22,\n            Name = \"Michael\",\n            Money = 236.66M\n        };\n\n        var mapperB = MapHelper.Map<MapperA, MapperB>(mapperA);\n        var mapperB1 = MapHelper.MapWith<MapperA, MapperB>(mapperA, \"Age\");\n        var mapperB2 = MapHelper.MapWithout<MapperA, MapperB>(mapperA, \"Age\");\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/NewtonJsonFormatter.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing Microsoft.Extensions.Logging;\nusing Microsoft.Extensions.Logging.Abstractions;\nusing Microsoft.Extensions.Logging.Console;\nusing Microsoft.Extensions.Options;\nusing Newtonsoft.Json;\n\nnamespace DotNetCoreSample;\n\npublic sealed class NewtonJsonFormatterOptions : ConsoleFormatterOptions\n{\n}\n\npublic sealed class NewtonJsonFormatter(IOptions<NewtonJsonFormatterOptions> options) : ConsoleFormatter(FormatterName)\n{\n    public const string FormatterName = \"NewtonJson\";\n\n    private readonly NewtonJsonFormatterOptions _options = options.Value;\n\n    public override void Write<TState>(in LogEntry<TState> logEntry, IExternalScopeProvider? scopeProvider, TextWriter textWriter)\n    {\n        var message = logEntry.Formatter(logEntry.State, logEntry.Exception);\n        if (logEntry.Exception == null && message == null)\n        {\n            return;\n        }\n\n        JsonWriter writer = new JsonTextWriter(textWriter);\n        writer.WriteStartObject();\n        if (_options.TimestampFormat != null)\n        {\n            writer.WritePropertyName(\"Timestamp\");\n            var timestamp = _options.UseUtcTimestamp ? DateTimeOffset.UtcNow : DateTimeOffset.Now;\n            var timestampText = timestamp.ToString(_options.TimestampFormat);\n            writer.WriteValue(timestampText);\n        }\n        writer.WritePropertyName(\"Level\");\n        writer.WriteValue(logEntry.LogLevel.ToString());\n        writer.WritePropertyName(\"EventId\");\n        writer.WriteValue(logEntry.EventId.ToString());\n        writer.WritePropertyName(nameof(logEntry.Category));\n        writer.WriteValue(logEntry.Category);\n        writer.WritePropertyName(\"Message\");\n        writer.WriteValue(message);\n        if (logEntry.Exception != null)\n        {\n            writer.WritePropertyName(\"Exception\");\n            writer.WriteValue(logEntry.Exception);\n        }\n\n        if (logEntry.State != null)\n        {\n            writer.WritePropertyName(nameof(logEntry.State));\n            writer.WriteStartObject();\n            writer.WritePropertyName(\"Message\");\n            writer.WriteValue(logEntry.State.ToString());\n            if (logEntry.State is System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>> stateProperties)\n            {\n                foreach (var item in stateProperties)\n                {\n                    writer.WritePropertyName(item.Key);\n                    writer.WriteValue(item.Value);\n                }\n            }\n            writer.WriteEndObject();\n        }\n\n        writer.WriteEndObject();\n        writer.Flush();\n        textWriter.WriteLine();\n    }\n}\n\npublic static partial class LoggingBuilderExtensions\n{\n    public static ILoggingBuilder AddNewtonJsonConsole(this ILoggingBuilder loggingBuilder,\n        Action<NewtonJsonFormatterOptions>? optionsConfigure = null)\n    {\n        loggingBuilder.AddConsole(options => options.FormatterName = NewtonJsonFormatter.FormatterName);\n        loggingBuilder.AddConsoleFormatter<NewtonJsonFormatter, NewtonJsonFormatterOptions>();\n        if (optionsConfigure != null)\n        {\n            loggingBuilder.Services.Configure(optionsConfigure);\n        }\n        return loggingBuilder;\n    }\n}\n\npublic static class NewtonJsonFormatterTest\n{\n    public static void MainTest()\n    {\n        var builder = Host.CreateEmptyApplicationBuilder(null);\n        // builder.Logging.AddJsonConsole();\n        builder.Logging.AddNewtonJsonConsole();\n        using var host = builder.Build();\n        host.Run();\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/PagedListResultTest.cs",
    "content": "﻿using WeihanLi.Common.Models;\n\nnamespace DotNetCoreSample;\n\npublic class PagedListResultTest\n{\n    public static void MainTest()\n    {\n        var listResult = new ListResultWithTotal<int>()\n        {\n            Data = Enumerable.Range(0, 10).ToArray(),\n            TotalCount = 20,\n        };\n        // GetEnumerator extensions for foreach\n        foreach (var res in listResult)\n        {\n            Console.WriteLine(res);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/PipelineTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\n\n// ReSharper disable LocalizableElement\n\nnamespace DotNetCoreSample;\n\npublic class PipelineTest\n{\n    public static void Test()\n    {\n        var requestContext = new RequestContext()\n        {\n            RequesterName = \"Kangkang\",\n            Hour = 12,\n        };\n\n        var builder = PipelineBuilder.Create<RequestContext>(context =>\n                {\n                    Console.WriteLine($\"{context.RequesterName} {context.Hour}h apply failed\");\n                })\n                .Use((context, next) =>\n                {\n                    if (context.Hour <= 2)\n                    {\n                        Console.WriteLine(\"pass 1\");\n                    }\n                    else\n                    {\n                        next();\n                    }\n                })\n                .Use((context, next) =>\n                {\n                    if (context.Hour <= 4)\n                    {\n                        Console.WriteLine(\"pass 2\");\n                    }\n                    else\n                    {\n                        next();\n                    }\n                })\n                .Use((context, next) =>\n                {\n                    if (context.Hour <= 6)\n                    {\n                        Console.WriteLine(\"pass 3\");\n                    }\n                    else\n                    {\n                        next();\n                    }\n                })\n            ;\n        var requestPipeline = builder.Build();\n        Console.WriteLine();\n        foreach (var i in Enumerable.Range(1, 8))\n        {\n            Console.WriteLine($\"--------- h:{i} apply Pipeline------------------\");\n            requestContext.Hour = i;\n            requestPipeline.Invoke(requestContext);\n            Console.WriteLine(\"----------------------------\");\n        }\n    }\n\n    public static void MiddlewareTest()\n    {\n        var context = new RequestContext();\n        var pipeline = PipelineBuilder.Create<RequestContext>()\n            .UseMiddleware<RequestContext, FooMiddleware>()\n            .UseMiddleware<RequestContext, BarMiddleware>()\n            .Build();\n        pipeline(context);\n    }\n\n    public static void TestV2()\n    {\n        var requestContext = new RequestContext()\n        {\n            RequesterName = \"Kangkang\",\n            Hour = 12,\n        };\n\n        var builder = PipelineBuilder.Create<RequestContext>(context =>\n                {\n                    Console.WriteLine($\"{context.RequesterName} {context.Hour}h apply failed\");\n                })\n                .Use((context, next) =>\n                {\n                    Console.WriteLine(\"Initialize\");\n                    next(context);\n                })\n                .When(context => context.Hour <= 2, pipeline =>\n                       {\n                           pipeline.Use((_, next) =>\n                           {\n                               Console.WriteLine(\"This should be invoked\");\n                               next();\n                           });\n                           pipeline.Run(_ => Console.WriteLine(\"pass 1\"));\n                           pipeline.Use((_, next) =>\n                           {\n                               Console.WriteLine(\"This should not be invoked\");\n                               next();\n                               Console.WriteLine(\"will this invoke?\");\n                           });\n                       })\n                .UseWhen(context => context.Hour > 2, pipeline =>\n                  {\n                      pipeline.Use((c, next) =>\n                      {\n                          Console.WriteLine(\"Use when middleware before\");\n                          next();\n                          Console.WriteLine(\"Use when middleware after\");\n                      });\n                  })\n                .When(context => context.Hour <= 4, pipeline =>\n                   {\n                       pipeline.Run(_ => Console.WriteLine(\"pass 2\"));\n                   })\n                .When(context => context.Hour <= 6, pipeline =>\n                   {\n                       pipeline.Run(_ => Console.WriteLine(\"pass 3\"));\n                   })\n\n            ;\n        var requestPipeline = builder.Build();\n        Console.WriteLine();\n        foreach (var i in Enumerable.Range(1, 8))\n        {\n            Console.WriteLine($\"--------- h:{i} apply Pipeline------------------\");\n            requestContext.Hour = i;\n            requestPipeline.Invoke(requestContext);\n            Console.WriteLine(\"----------------------------\");\n        }\n    }\n\n    public static async Task AsyncPipelineBuilderTest()\n    {\n        var requestContext = new RequestContext()\n        {\n            RequesterName = \"Michael\",\n            Hour = 12,\n        };\n\n        var builder = PipelineBuilder.CreateAsync<RequestContext>(context =>\n                {\n                    Console.WriteLine($\"{context.RequesterName} {context.Hour}h apply failed\");\n                    return Task.CompletedTask;\n                })\n                .Use(async (context, next) =>\n                {\n                    if (context.Hour <= 2)\n                    {\n                        Console.WriteLine(\"pass 1\");\n                    }\n                    else\n                    {\n                        await next();\n                    }\n                })\n                .Use(async (context, next) =>\n                {\n                    if (context.Hour <= 4)\n                    {\n                        Console.WriteLine(\"pass 2\");\n                    }\n                    else\n                    {\n                        await next();\n                    }\n                })\n                .Use(async (context, next) =>\n                {\n                    if (context.Hour <= 6)\n                    {\n                        Console.WriteLine(\"pass 3\");\n                    }\n                    else\n                    {\n                        await next();\n                    }\n                })\n            ;\n        var requestPipeline = builder.Build();\n        Console.WriteLine();\n        foreach (var i in Enumerable.Range(1, 8))\n        {\n            Console.WriteLine($\"--------- h:{i} apply AsyncPipeline------------------\");\n            requestContext.Hour = i;\n            await requestPipeline.Invoke(requestContext);\n            Console.WriteLine(\"----------------------------\");\n        }\n    }\n\n    public static async Task AsyncPipelineBuilderTestV2()\n    {\n        var requestContext = new RequestContext()\n        {\n            RequesterName = \"Michael\",\n            Hour = 12,\n        };\n\n        var builder = PipelineBuilder.CreateAsync<RequestContext>(context =>\n                {\n                    Console.WriteLine($\"{context.RequesterName} {context.Hour}h apply failed\");\n                    return Task.CompletedTask;\n                })\n                .When(context => context.Hour <= 2, pipeline =>\n                {\n                    pipeline.Run(_ =>\n                    {\n                        Console.WriteLine(\"pass 1\");\n                        return Task.CompletedTask;\n                    });\n                })\n                .When(context => context.Hour <= 4, pipeline =>\n                {\n                    pipeline.Run(_ =>\n                    {\n                        Console.WriteLine(\"pass 2\");\n                        return Task.CompletedTask;\n                    });\n                })\n                .When(context => context.Hour <= 6, pipeline =>\n                {\n                    pipeline.Run(_ =>\n                    {\n                        Console.WriteLine(\"pass 3\");\n                        return Task.CompletedTask;\n                    });\n                })\n            ;\n        var requestPipeline = builder.Build();\n        Console.WriteLine();\n        foreach (var i in Enumerable.Range(1, 8))\n        {\n            Console.WriteLine($\"--------- h:{i} apply AsyncPipeline------------------\");\n            requestContext.Hour = i;\n            await requestPipeline.Invoke(requestContext);\n            Console.WriteLine(\"----------------------------\");\n        }\n    }\n}\nfile class RequestContext\n{\n    public string? RequesterName { get; set; }\n\n    public int Hour { get; set; }\n}\n\nfile class FooMiddleware : IPipelineMiddleware<RequestContext>, IAsyncPipelineMiddleware<RequestContext>\n{\n    public void Invoke(RequestContext context, Action<RequestContext> next)\n    {\n        Console.WriteLine(\"I'm foo\");\n        next(context);\n    }\n\n    public Task InvokeAsync(RequestContext context, Func<RequestContext, Task> next)\n    {\n        Console.WriteLine(\"I'm foo\");\n        return next(context); ;\n    }\n}\n\nfile class BarMiddleware : IPipelineMiddleware<RequestContext>, IAsyncPipelineMiddleware<RequestContext>\n{\n    public void Invoke(RequestContext context, Action<RequestContext> next)\n    {\n        Console.WriteLine(\"I'm bar\");\n        next(context);\n    }\n\n    public Task InvokeAsync(RequestContext context, Func<RequestContext, Task> next)\n    {\n        Console.WriteLine(\"I'm bar\");\n        return next(context); ;\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/ProcessExecutorTest.cs",
    "content": "﻿using System.Diagnostics;\nusing WeihanLi.Common.Helpers;\n\nnamespace DotNetCoreSample;\n\npublic class ProcessExecutorTest\n{\n    public static void RawProcessTest()\n    {\n        using var process = new Process()\n        {\n            StartInfo = new ProcessStartInfo(\"dotnet\", \"--info\"),\n            EnableRaisingEvents = true,\n        };\n        process.StartInfo.UseShellExecute = false;\n        process.StartInfo.RedirectStandardOutput = true;\n        process.StartInfo.RedirectStandardInput = true;\n        process.StartInfo.RedirectStandardError = true;\n\n        process.OutputDataReceived += (sender, args) =>\n        {\n            Console.WriteLine(args.Data);\n        };\n        process.Exited += (sender, args) =>\n        {\n            if (sender is Process _process)\n            {\n                Console.WriteLine($\"The Process({_process.Id}) exited with code({_process.ExitCode})\");\n            }\n        };\n        process.Start();\n        process.BeginOutputReadLine();\n\n        process.WaitForExit();\n    }\n\n    public static void DotNetInfoTest()\n    {\n        using var executor = new ProcessExecutor(\"dotnet\", \"--info\");\n\n        executor.OnOutputDataReceived += (sender, str) =>\n        {\n            Console.WriteLine(str);\n        };\n        executor.Execute();\n    }\n\n    public static void DotNetNugetGlobalPackagesInfoTest()\n    {\n        using var executor = new ProcessExecutor(\"dotnet\", \"nuget locals global-packages -l\");\n        var folder = string.Empty;\n        executor.OnOutputDataReceived += (sender, str) =>\n        {\n            if (str is null)\n                return;\n\n            Console.WriteLine(str);\n\n            if (str.StartsWith(\"global-packages:\"))\n            {\n                folder = str[\"global-packages:\".Length..].Trim();\n            }\n        };\n        executor.Execute();\n\n        System.Console.WriteLine(folder);\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/Program.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing DotNetCoreSample;\nusing System.Net;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\n\nConsoleHelper.WriteLineWithColor(\"----------DotNetCoreSample----------\", ConsoleColor.DarkGreen);\n\n{\n    var text = \"127.0.0.1\";\n    var ip = text.To<IPAddress>();\n    Console.WriteLine(ip);\n}\n\n\n// InvokeHelper.OnInvokeException = ex => ConsoleHelper.ErrorWriteLineWithColor(ex.ToString(), ConsoleColor.DarkRed);\n\n// ServiceDecoratorTest.MainTest();\n\n// var dataLogger = LogHelper.GetLogger(typeof(DataExtension));\n// DataExtension.CommandLogAction = msg => dataLogger.Debug(msg);\n\n//AspectTest.ServiceContainerTest();\n\n//ProcessExecutorTest.RawProcessTest();\n//ProcessExecutorTest.DotNetInfoTest();\n// ProcessExecutorTest.DotNetNugetGlobalPackagesInfoTest();\n// PipelineTest.TestV2();\n\n// new[] { 1, 2, 3 }.GetCombinations(2).Dump();\n// new[] { 1, 2, 3 }.GetCombinations(2, true).Dump();\n//\n// new[] { 1, 2, 3, 2 }.GetPermutations().Dump();\n// new[] { 1, 2, 3, 2 }.GetPermutations(true).Dump();\n//\n\n// CommandExecutorTest.MainTest();\n\n//LoggerTest.MainTest();\n// LoggerTest.MicrosoftLoggingTest();\n\n// Console.ReadLine();\n\n// var services = new ServiceCollection();\n// services.AddTransient<IFly, MonkeyKing>();\n// IConfiguration configuration = new ConfigurationBuilder()\n//     .AddJsonFile(\"appsettings.json\")\n//     .Build();\n//\n// var city = configuration.GetAppSetting(\"City\");\n// var number = configuration.GetAppSetting<int>(\"Number\");\n// Console.WriteLine($\"City:{city}, Number:{number}\");\n//\n// services.AddSingleton(configuration);\n\n//services.AddSingleton<IEventStore, EventStoreInMemory>();\n//services.AddSingleton<IEventBus, EventBus>();\n\n//services.AddSingleton(DelegateEventHandler.FromAction<CounterEvent>(@event =>\n//    LogHelper.GetLogger(typeof(DelegateEventHandler<CounterEvent>))\n//        .Info($\"Event Info: {@event.ToJson()}\")\n//    )\n//);\n//\n// services.AddSingletonProxy<IFly, MonkeyKing>();\n//\n// Action<DbContextOptionsBuilder> dbContextOptionsAction = options =>\n// {\n//     options.UseInMemoryDatabase(\"Test\");\n// };\n// services.AddDbContext<TestDbContext>(dbContextOptionsAction);\n//\n// services.AddScopedProxy<TestDbContext>();\n// services.AddEvents();\n//\n// services.AddSingletonProxy<IEventBus, EventBus>();\n// services.AddFluentAspects(options =>\n//     {\n//         options.NoInterceptPropertyGetter<IFly>(f => f.Name);\n//\n//         options.InterceptAll()\n//             .With<LogInterceptor>()\n//             ;\n//         options.InterceptMethod<DbContext>(x => x.Name == nameof(DbContext.SaveChanges)\n//                                                 || x.Name == nameof(DbContext.SaveChangesAsync))\n//             .With<DbContextSaveInterceptor>()\n//             ;\n//         options.InterceptMethod<IFly>(f => f.Fly())\n//             .With<LogInterceptor>();\n//         options.InterceptType<IFly>()\n//             .With<LogInterceptor>();\n//\n//         options\n//             .WithProperty(\"TraceId\", \"121212\")\n//             ;\n//     })\n//     // .UseCastleProxy()\n//     // .UseAspectCoreProxy()\n//     ;\n//\n// DependencyResolver.SetDependencyResolver(services);\n\n//var fly = DependencyResolver.ResolveService<IFly>();\n//Console.WriteLine(fly.Name);\n//fly.Fly();\n//fly.OpenFly<int>();\n//fly.OpenFly<string>();\n//fly.FlyAway();\n\n//var animal1 = FluentAspects.AspectOptions.ProxyFactory.CreateInterfaceProxy<IAnimal<int>>();\n//animal1.Eat();\n\n//var animal2 = FluentAspects.AspectOptions.ProxyFactory.CreateInterfaceProxy<IAnimal<string>>();\n//animal2.Eat();\n\n//var animal = FluentAspects.AspectOptions.ProxyFactory.CreateProxy<Animal<string>>();\n//animal.Eat();\n//animal.Eat();\n//Console.WriteLine(animal.GetEatCount());\n//animal.Drink(\"xxx\");\n//Console.WriteLine(animal.GetDrinkCount());\n\n//animal = FluentAspects.AspectOptions.ProxyFactory.CreateProxyWithTarget<Animal<string>>(new Animal<string>());\n//animal.Eat();\n//animal.Eat();\n//Console.WriteLine(animal.GetEatCount());\n//animal.Drink(\"xxx\");\n//Console.WriteLine(animal.GetDrinkCount());\n\n//DependencyResolver.TryInvokeService<TestDbContext>(dbContext =>\n//{\n//    dbContext.TestEntities.Add(new TestEntity() { Token = \"sasa\", CreatedTime = DateTime.Now, });\n//    var hasChanges = dbContext.ChangeTracker.HasChanges();\n//    Console.WriteLine($\"hasChanges：{hasChanges}\");\n//    dbContext.SaveChanges();\n//});\n\n//DependencyResolver.TryInvokeService<IEventBus>(eventBus =>\n//{\n//    eventBus.Publish(new CounterEvent());\n//});\n\n// DependencyResolver.TryInvoke<IProxyFactory>(proxyFactory =>\n// {\n//     var counterEvent = new CounterEvent() { Counter = 1 };\n//     var eventBusProxy = proxyFactory.CreateProxy<IEventBus, EventBus>();\n//     eventBusProxy.Publish(counterEvent);\n//\n//     var handlerProxy = proxyFactory.CreateProxyWithTarget<IEventHandler<CounterEvent>>(DelegateEventHandler.FromAction<CounterEvent>(e =>\n//     {\n//         Console.WriteLine(e.ToJson());\n//     }));\n//     handlerProxy.Handle(counterEvent)\n//         .GetAwaiter().GetResult();\n// });\n\n//DependencyResolver.TryInvokeServiceAsync<TestDbContext>(dbContext =>\n//{\n//    dbContext.TestEntities.Add(new TestEntity() { Token = \"sasa\", CreatedTime = DateTime.Now, });\n\n//    if (dbContext.ChangeTracker is null)\n//    {\n//        Console.WriteLine($\"{nameof(dbContext.ChangeTracker)} is null ...\");\n//    }\n//    else\n//    {\n//        foreach (var entry in dbContext.ChangeTracker.Entries<TestEntity>())\n//        {\n//            Console.WriteLine(entry.Entity.Token);\n//        }\n//    }\n\n//    if (dbContext.Database is null)\n//    {\n//        Console.WriteLine($\"{nameof(dbContext.Database)} is null ...\");\n//    }\n//    if (dbContext.Database is null)\n//    {\n//        Console.WriteLine($\"{nameof(dbContext.Database)} is null ...\");\n//    }\n\n//    return dbContext.SaveChangesAsync();\n//});\n\n//DependencyResolver.ResolveRequiredService<IFly>()\n//    .Fly();\n\n//DependencyInjectionTest.Test();\n\n// EventTest.MainTest();\n\n// SerilogTest.MainTest();\n\n//var builder = new ContainerBuilder();\n//builder.RegisterType<MonkeyKing>().As<IFly>();\n\n//DependencyResolver.SetDependencyResolver((IServiceProvider)new AutofacDependencyResolver(builder.Build()));\n\n//DependencyInjectionTest.Test();\n\n//int a = 1;\n//Console.WriteLine(JsonConvert.SerializeObject(a));// output 1\n\n//var pagedListModel = new PagedListModel<int>()\n//{\n//    PageNumber = 1,\n//    PageSize = 4,\n//    TotalCount = 10,\n//    Data = new[] { 1, 2, 3, 4 }\n//};\n//Console.WriteLine(pagedListModel.ToJson());\n\n// log test\n// LoggerTest.MainTest();\n\n//ILoggerFactory loggerFactory = new LoggerFactory();\n//loggerFactory.AddConsole();\n//loggerFactory.AddDebug();\n//loggerFactory.AddDelegateLogger(\n//    (category, logLevel, exception, msg) =>\n//    {\n//        Console.WriteLine($\"{category}:[{logLevel}] {msg}\\n{exception}\");\n//    }\n//);\n\n//var logger = new Logger<Program>(loggerFactory);\n//logger.LogInformation(\"Logging information from Microsoft.Extensions.Logging\");\n\n//InvokeHelper.TryInvoke(DataExtensionTest.MainTest);\n\n//TaskTest.TaskWhenAllTest().GetAwaiter().GetResult();\n\n//Base64UrlEncodeTest.MainTest();\n\n//var a = new { Name = \"2123\" };\n//var name = a.GetProperty(\"Name\").GetValueGetter().Invoke(a);\n//Console.WriteLine(name);\n\n//var structTest = new TestStruct() { Name = \"1233\" };\n//var obj = (object)structTest;\n//Console.WriteLine(structTest.GetProperty(\"Name\").GetValueGetter<TestStruct>().Invoke(structTest));\n//structTest.GetProperty(\"Name\").GetValueSetter().Invoke(obj, \"Name1\");\n//structTest = (TestStruct)obj;\n\n//Console.WriteLine(structTest.Name);\n\n//Expression<Func<TestEntity, bool>> exp = t => t.Id > 10 && t.Token == \"123\" && t.Token.Contains(\"12\");\n//var str = SqlExpressionParser.ParseExpression(exp);\n//Console.WriteLine(\"sql: {0}\", str);\n\n//exp = t => true;\n//str = SqlExpressionParser.ParseExpression(exp);\n//Console.WriteLine(\"sql: {0}\", str);\n\n//RepositoryTest.MainTest();\n\n//var securityToken = ApplicationHelper.ApplicationName + \"_test_123\";\n//var code123 = TotpHelper.GenerateCode(securityToken);\n//Console.WriteLine(code123);\n//var result = TotpHelper.ValidateCode(securityToken, code123);\n//Console.WriteLine($\"validate result: {result}\");\n\n//var ttl = 2;\n//while (ttl > 1)\n//{\n//    ttl = TotpHelper.TTL(securityToken);\n//    Console.WriteLine($\"Current ttl: {ttl}, newId: {ObjectIdGenerator.Instance.NewId()}\");\n//    Thread.Sleep(1000);\n//}\n//result = TotpHelper.ValidateCode(securityToken, code123);\n//Console.WriteLine($\"validate result1: {result}\");\n\n//result = TotpHelper.ValidateCode(securityToken, code123, 60);\n//Console.WriteLine($\"validate result2: {result}\");\n//var code1234 = TotpHelper.GenerateCode(ApplicationHelper.ApplicationName + \"test_1234\");\n//Console.WriteLine(code1234);\n\n// InvokeHelper.TryInvoke(HttpRequesterTest.MainTest);\n\n//var pagedListModel = new PagedListModel<int>()\n//{\n//    PageNumber = 2, PageSize = 2, TotalCount = 6, Data = new int[] {1, 2},\n//};\n//var pagedListModel1 = new PagedListModel1<int>()\n//{\n//    PageNumber = 2,\n//    PageSize = 2,\n//    TotalCount = 6,\n//    Data = new int[] { 1, 2 },\n//};\n//Console.WriteLine($\"pagedListModel:{JsonConvert.SerializeObject(pagedListModel)}, pagedListModel1:{JsonConvert.SerializeObject(pagedListModel1)}\");\n\n//var posts = new[] { new { PostId = 1, PostTitle = \"12333\", }, new { PostId = 2, PostTitle = \"12333\", }, };\n//var postTags = new[] { new { PostId = 1, Tag = \"HHH\" } };\n\n//var result = posts.LeftJoin(postTags, p => p.PostId, pt => pt.PostId, (p, pt) => new { p.PostId, p.PostTitle, pt?.Tag }).ToArray();\n//Console.WriteLine(result.ToJson());\n\n// CronHelperTest.MainTest();\n\n// DependencyInjectionTest.BuiltInIocTest();\n\n//Expression<Func<TestEntity, bool>> expression =\n//    t => t.Id > 0 && t.CreatedTime < DateTime.Now && t.Token == null;\n//var visitor = new SqlExpressionVisitor(null);\n//visitor.Visit(expression);\n//Console.WriteLine(visitor.GetCondition());\n\n//PipelineTest.TestV2();\n//PipelineTest.AsyncPipelineBuilderTestV2().Wait();\n\n//TotpTest.MainTest();\n\n// exit test\n// var exitToken = ApplicationHelper.ExitToken;\n// exitToken.Register(() =>\n// {\n//     Console.WriteLine(@\"Exiting\");\n//     Thread.Sleep(3000);\n//     Console.WriteLine(@\"Exited\");\n// });\n// ApplicationHelper.RuntimeInfo.Dump();\n// Console.WriteLine(ApplicationHelper.ResolvePath(\"yarn.cmd\"));\n// await AppHostTest.MainTest();\n// NewtonJsonFormatterTest.MainTest();\n\n// DisposeTest.MainTest();\n// Console.WriteLine();\n// await DisposeTest.MainTestAsync();\n// Console.WriteLine();\n//\n// ConsoleHelper.ReadKeyWithPrompt(\"Press any key to continue\");\n//\n// await DisposeTest.MainTestAsync();\n// Console.WriteLine();\n//\n// GC.Collect();\n// GC.WaitForPendingFinalizers();\n\n// await CommandExecutor.ExecuteCommandAndOutputAsync(\n//     \"dotnet build \\\"C:\\\\projects\\\\sources\\\\dotnet-exec\\\\src\\\\dotnet-exec\\\\dotnet-exec.csproj\\\"\");\n\n\n// {\n//     var cts = CancellationTokenSource.CreateLinkedTokenSource(ApplicationHelper.ExitToken, default);\n//     var registration = cts.Token.Register(() => Console.WriteLine(@\"Exited\"));\n//     cts.Dispose();\n//     // registration would be disposed when cts dispose\n// }\n\n// await InvokeHelper.TryInvokeAsync(EventTest.MainTest);\n\n// InvokeHelper.TryInvoke(CommandExecutorTest.MainTest);\n\n// InvokeHelper.TryInvoke(() => throw null, 3);\n\n// InvokeHelper.TryInvoke(LoggerTest.MicrosoftLoggingTest);\n// await InvokeHelper.TryInvokeAsync(EventTest.AckQueueTest);\n\nConsoleHelper.ReadKeyWithPrompt(\"Press any key to exit\");\n\ninternal struct TestStruct\n{\n    public string? Name { get; set; }\n}\n\ninternal class TestClass\n{\n    public string? Name { get; set; }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/RepositoryTest.cs",
    "content": "﻿using Microsoft.Data.SqlClient;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.ObjectPool;\nusing System.Data.Common;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Data;\nusing WeihanLi.Extensions;\n\nnamespace DotNetCoreSample;\n\npublic static class RepositoryTest\n{\n    public static void MainTest()\n    {\n        var connectionPool = new DbConnectionPool(new DbConnectionPoolPolicy(DependencyResolver.ResolveRequiredService<IConfiguration>()\n            .GetRequiredConnectionString(\"Test\")!));\n\n        var repo = new Repository<TestEntity>(() => connectionPool.Get());\n        repo.Execute(\"TRUNCATE TABLE dbo.tabTestEntity\");\n\n        repo.Insert(new TestEntity\n        {\n            Token = \"1233\",\n            CreatedTime = DateTime.UtcNow\n        });\n\n        var entity = repo.Fetch(t => t.Id == 1);\n        System.Console.WriteLine(entity?.Token);\n\n        repo.Update(t => t.Id == 1, t => t.Token, 1);\n\n        entity = repo.Fetch(t => t.Id == 1);\n        System.Console.WriteLine(entity?.Token);\n\n        var exists = repo.Exist(e => e.Id == 1);\n        Console.WriteLine($\"exists pkid == 1: {exists}\");\n\n        repo.Delete(t => t.Id == 1);\n        entity = repo.Fetch(t => t.Id == 1);\n        System.Console.WriteLine($\"delete operation {(entity == null ? \"Success\" : \"Failed\")}\");\n\n        exists = repo.Exist(e => e.Id > 1000);\n        Console.WriteLine($\"exists PKID > 1000: {exists}\");\n        repo.Execute(\"TRUNCATE TABLE dbo.tabTestEntity\");\n\n        var data = repo.Paged(1, 10, t => t.Id > 10, t => t.CreatedTime, true);\n        Console.WriteLine($\"TotalCount: {data.TotalCount}, dataCount: {data.Count}\");\n\n        Console.WriteLine(\"finished.\");\n    }\n\n    public class DbConnectionPool : DefaultObjectPool<DbConnection>\n    {\n        public DbConnectionPool(IPooledObjectPolicy<DbConnection> policy) : base(policy)\n        {\n        }\n\n        public DbConnectionPool(IPooledObjectPolicy<DbConnection> policy, int maximumRetained) : base(policy, maximumRetained)\n        {\n        }\n    }\n\n    public class DbConnectionPoolPolicy(string connString) : IPooledObjectPolicy<DbConnection>\n    {\n        private readonly string _connString = connString;\n\n        public DbConnection Create()\n        {\n            return new SqlConnection(_connString);\n        }\n\n        public bool Return(DbConnection obj)\n        {\n            return obj.ConnectionString.IsNotNullOrWhiteSpace();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/RequestHelperTest.cs",
    "content": "﻿using System.Collections.Specialized;\nusing System.Diagnostics;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Logging;\n\nnamespace DotNetCoreSample;\n\ninternal class RequestHelperTest\n{\n    private static readonly ILogHelperLogger Logger = LogHelper.GetLogger<RequestHelperTest>();\n\n    public static void GetRequestParamTest()\n    {\n        var param = GetParamInfo(\"https://www.baidu.com/?tn=47018152_dg\");\n        Debug.Assert(param.HasKeys());\n        Debug.Assert(param.Count == 1);\n\n        param = GetParamInfo(\"http://tieba.baidu.com/f/index/forumpark?pcn=%E5%B0%8F%E8%AF%B4&pci=161&ct=0&rn=20&pn=1\");\n        Debug.Assert(param.HasKeys());\n        Debug.Assert(param.Count == 5);\n\n        param = GetParamInfo(\"https://www.baidu.com/?tn\");\n        Debug.Assert(param.HasKeys());\n\n        param = GetParamInfo(\"https://www.baidu.com/?tn=\");\n        Debug.Assert(param[\"tn\"] == string.Empty);\n    }\n\n    private static NameValueCollection GetParamInfo(string url)\n    {\n        var param = RequestHelper.GetParamCollection(url);\n        Logger.Debug(\"\\n url:{0} \\n param info:\\n {1}\", url, string.Join(\",\", param.AllKeys.Select(p => p + \":\" + param.Get(p))));\n        return param;\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/SerilogTest.cs",
    "content": "﻿using Microsoft.Extensions.Logging;\nusing Serilog;\nusing WeihanLi.Common.Logging.Serilog;\n\nnamespace DotNetCoreSample;\n\npublic static class SerilogTest\n{\n    public static void MainTest()\n    {\n        ILoggerFactory loggerFactory = new LoggerFactory();\n        SerilogHelper.LogInit(config => config.WriteTo.Console());\n        loggerFactory.AddSerilog();\n\n        var logger = loggerFactory.CreateLogger(typeof(SerilogTest));\n        logger.LogTrace(\"1111111\");\n        logger.LogDebug(\"1111111\");\n        logger.LogInformation(\"121212331\");\n        logger.LogWarning(\"121212331\");\n        logger.LogError(\"121212331\");\n        logger.LogCritical(\"1213131\");\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/ServiceDecoratorTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace DotNetCoreSample;\n\ninternal class ServiceDecoratorTest\n{\n    public static void MainTest()\n    {\n        var services = new ServiceCollection();\n        services.AddSingleton<IJob, Sleepy>();\n        services.Decorate<IJob, JobDecorator>();\n        using var sp = services.BuildServiceProvider();\n\n        var job = sp.GetRequiredService<IJob>();\n        Console.WriteLine(job.Name);\n        job.Execute();\n    }\n\n    private interface IJob\n    {\n        string Name { get; }\n        void Execute();\n    }\n\n    private sealed class Sleepy : IJob\n    {\n        public string Name => nameof(Sleepy);\n\n        public void Execute()\n        {\n            Console.WriteLine(\"Sleeping...\");\n        }\n    }\n\n    private sealed class JobDecorator(IJob job) : IJob\n    {\n        private readonly IJob _job = job;\n\n        public string Name => $\"??? {_job.Name}\";\n\n        public void Execute()\n        {\n            Console.WriteLine(\"Before execute\");\n\n            _job.Execute();\n\n            Console.WriteLine(\"After execute\");\n        }\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/TaskTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\n\nnamespace DotNetCoreSample;\n\ninternal static class TaskTest\n{\n    public static async Task TaskWhenAllTest()\n    {\n        ThreadPool.GetMaxThreads(out var workerThreads, out var completionPortThreads);\n        Console.WriteLine($\"threadpool max threads setting:workerThreads:{workerThreads}, completionPortThreads:{completionPortThreads}\");\n\n        var time = await InvokeHelper.ProfileAsync(() => Task.WhenAll(Enumerable.Range(1, 5).Select(_ => Task.Delay(1000))));\n        Console.WriteLine(\"await Task.WhenAll time:{0} ms\", time);\n\n        time = InvokeHelper.Profile(() => Task.WhenAll(Enumerable.Range(1, 5).Select(_ => Task.Delay(1000))));\n        Console.WriteLine(\"Task.WhenAll no wait time:{0} ms\", time);\n\n        time = InvokeHelper.Profile(() => Task.WhenAll(Enumerable.Range(1, 5).Select(_ => Task.Delay(1000))).Wait());\n        Console.WriteLine(\"Task.WhenAll wait time:{0} ms\", time);\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/TemplatingSample.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing WeihanLi.Common.Template;\n\nnamespace DotNetCoreSample;\n\npublic static class TemplatingSample\n{\n    public static async Task MainTest()\n    {\n        {\n            var engine = TemplateEngine.CreateDefault();\n            var result = await engine.RenderAsync(\"Hello {{Name}}\", new { Name = \".NET\" });\n            Console.WriteLine(result);\n            Console.WriteLine(await engine.RenderAsync(\"Hello {{Name | toTitle }}\", new { Name = \"mike\" }));\n            Console.WriteLine(await engine.RenderAsync(\"Today is {{ date | format:yyyy-MM-dd }}\", new { date = DateTime.Today }));\n        }\n\n        {\n            var result = await TemplateEngine.CreateDefault().RenderAsync(\"Hello {{$env USERNAME}}\");\n            Console.WriteLine(result);\n        }\n\n        {\n            var configuration = new ConfigurationBuilder()\n                .AddInMemoryCollection([new(\"UserName\", \"Test\")])\n                .Build();\n            var result = await TemplateEngine.CreateDefault(builder => builder.ConfigureOptions(options => options.Configuration = configuration))\n                .RenderAsync(\"Hello {{$config UserName}}\");\n            Console.WriteLine(result);\n        }\n\n        {\n            var services = new ServiceCollection();\n            IConfiguration configuration = new ConfigurationBuilder()\n                .AddInMemoryCollection([new(\"UserName1\", \"Test1234\")])\n                .Build();\n            services.AddSingleton(configuration);\n            services.AddTemplateEngine();\n            await using var provider = services.BuildServiceProvider();\n            var result = await provider.GetRequiredService<ITemplateEngine>()\n                .RenderAsync(\"Hello {{$config UserName1}}\");\n            Console.WriteLine(result);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/Test/PagedListModel1.cs",
    "content": "﻿using System.Collections;\n\nnamespace DotNetCoreSample.Test;\n\npublic class PagedListModel1<T> : IEnumerable<T>\n{\n    public IReadOnlyList<T> Data { get; set; } = Array.Empty<T>();\n\n    private int _pageNumber = 1;\n\n    public int PageNumber\n    {\n        get => _pageNumber;\n        set\n        {\n            if (value > 0)\n            {\n                _pageNumber = value;\n            }\n        }\n    }\n\n    private int _pageSize = 10;\n\n    public int PageSize\n    {\n        get => _pageSize;\n        set\n        {\n            if (value > 0)\n            {\n                _pageSize = value;\n            }\n        }\n    }\n\n    private int _totalCount;\n\n    public int TotalCount\n    {\n        get => _totalCount;\n        set\n        {\n            if (value > 0)\n            {\n                _totalCount = value;\n            }\n        }\n    }\n\n    public int PageCount => Convert.ToInt32(Math.Ceiling(_totalCount * 1.0 / _pageSize));\n\n    public T this[int index] => Data[index];\n\n    public int Count => Data.Count;\n\n    public IEnumerator<T> GetEnumerator()\n    {\n        return Data.GetEnumerator();\n    }\n\n    IEnumerator IEnumerable.GetEnumerator()\n    {\n        return Data.GetEnumerator();\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/TotpTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Otp;\n\nnamespace DotNetCoreSample;\n\npublic class TotpTest\n{\n    public static void MainTest()\n    {\n        var secret = \"xx\";\n        var totp = new Totp();\n        while (true)\n        {\n            var (Code, Ttl) = totp.ComputeWithTtl(Base32EncodeHelper.GetBytes(secret));\n            Console.WriteLine(@$\"{Code} {Ttl}\");\n            ConsoleHelper.ReadLineWithPrompt();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/DotNetCoreSample/appsettings.json",
    "content": "{\n  \"ConnectionStrings\": {\n    \"TestDb\": \"server=.;database=Test;uid=weihanli;pwd=Admin888\",\n    \"Test\": \"server=.;database=Test;Integrated Security=True\"\n  },\n  \"AppSettings\": {\n    \"Number\": 12,\n    \"City\": \"Shanghai\"\n  }\n}"
  },
  {
    "path": "src/Directory.Build.props",
    "content": "<Project>\n  <Import Project=\"$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))\" />\n  <Import Project=\"../build/sign.props\" />\n  <PropertyGroup>\n    <!-- code analysis https://github.com/dotnet/docs/blob/main/docs/core/project-sdk/msbuild-props.md#code-analysis-properties -->\n    <!-- <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>\n    <AnalysisLevel>latest</AnalysisLevel>\n    <AnalysisMode>Recommended</AnalysisMode>\n    <AnalysisModeSecurity>All</AnalysisModeSecurity> -->\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <RepositoryUrl>https://github.com/WeihanLi/WeihanLi.Common</RepositoryUrl>\n    <!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->\n    <PublishRepositoryUrl>true</PublishRepositoryUrl>\n    <!-- Optional: Embed source files that are not tracked by the source control manager in the PDB -->\n    <EmbedUntrackedSources>true</EmbedUntrackedSources>\n    <!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->\n    <IncludeSymbols>true</IncludeSymbols>\n    <SymbolPackageFormat>snupkg</SymbolPackageFormat>\n    <IsPackable>true</IsPackable>\n    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>\n    <PackageReleaseNotes>https://github.com/WeihanLi/WeihanLi.Common/releases</PackageReleaseNotes>\n    <PackageTags>$(PackageTags);WeihanLi</PackageTags>\n    <PackageIcon>icon.jpg</PackageIcon>\n    <PackageReadmeFile>README.md</PackageReadmeFile>\n    <PackageLicenseExpression>MIT</PackageLicenseExpression>\n    <LangVersion>preview</LangVersion>\n    <NoWarn>$(NoWarn);CS9216;</NoWarn>\n    <NuGetAuditMode>direct</NuGetAuditMode>\n  </PropertyGroup>\n  <ItemGroup>\n    <Using Include=\"System.Object\" Alias=\"Lock\" Condition=\"!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))\" />\n    <None Include=\"README.md\" Pack=\"true\" PackagePath=\"\\\" />\n    <None Include=\"$([MSBuild]::GetPathOfFileAbove('icon.jpg', '$(MSBuildThisFileDirectory)../'))\" Pack=\"true\" Visible=\"false\" PackagePath=\"\"/>\n  </ItemGroup>\n  <ItemGroup>\n    <InternalsVisibleTo Condition=\"'$(Configuration)'=='Debug'\" Include=\"WeihanLi.Common.Test\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "src/WeihanLi.Common/Abstractions/Properties.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Abstractions;\n\npublic interface IProperties\n{\n    IDictionary<string, object?> Properties { get; }\n}\n\npublic interface IStringProperties\n{\n    IDictionary<string, string> Properties { get; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/AspectDelegate.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic static class AspectDelegate\n{\n    [RequiresUnreferencedCode(\"Unreferenced code may be used.\")]\n    public static void Invoke(IInvocation context)\n    {\n        InvokeInternal(context, null, null);\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used.\")]\n    public static void InvokeWithInterceptors(IInvocation invocation, IReadOnlyList<IInterceptor>? interceptors)\n    {\n        InvokeInternal(invocation, interceptors, null);\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used.\")]\n    public static void InvokeWithCompleteFunc(IInvocation invocation, Func<IInvocation, Task>? completeFunc)\n    {\n        InvokeInternal(invocation, null, completeFunc);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static void InvokeInternal(IInvocation invocation, IReadOnlyList<IInterceptor>? interceptors, Func<IInvocation, Task>? completeFunc)\n    {\n        // enrich\n        foreach (var enricher in FluentAspects.AspectOptions.Enrichers)\n        {\n            try\n            {\n                enricher.Enrich(invocation);\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n        }\n\n        // invoke delegate\n        var action = GetAspectDelegate(invocation, interceptors, completeFunc);\n        var task = action.Invoke(invocation);\n        if (!task.IsCompleted)\n        {\n            // await task to be completed\n            task.ConfigureAwait(false).GetAwaiter().GetResult();\n        }\n        if (task.Exception != null)\n        {\n            var exception = task.Exception.Unwrap();\n            throw exception;\n        }\n\n        // ensure return value\n        if (invocation.ProxyMethod.ReturnType != typeof(void) && invocation.ReturnValue == null)\n        {\n            if (invocation.ProxyMethod.ReturnType.IsValueType)\n            {\n                invocation.ReturnValue = invocation.ProxyMethod.ReturnType.GetDefaultValue();\n            }\n\n            if (invocation.ProxyMethod.ReturnType == typeof(Task))\n            {\n                invocation.ReturnValue = Task.CompletedTask;\n            }\n\n            if (invocation.ProxyMethod.ReturnType.IsGenericType\n                && invocation.ProxyMethod.ReturnType.IsAssignableTo<Task>())\n            {\n                var resultType = invocation.ProxyMethod.ReturnType.GetGenericArguments()[0];\n                invocation.ReturnValue = Task.FromResult(resultType.GetDefaultValue());\n            }\n\n            if (invocation.ProxyMethod.ReturnType == typeof(ValueTask))\n            {\n                invocation.ReturnValue = default(ValueTask);\n            }\n            if (invocation.ProxyMethod.ReturnType.IsGenericType\n                && invocation.ProxyMethod.ReturnType.IsAssignableTo<ValueTask>())\n            {\n                var resultType = invocation.ProxyMethod.ReturnType.GetGenericArguments()[0];\n                invocation.ReturnValue = new ValueTask(Task.FromResult(resultType.GetDefaultValue()));\n            }\n        }\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    private static Func<IInvocation, Task> GetAspectDelegate(IInvocation invocation, IReadOnlyList<IInterceptor>? interceptors, Func<IInvocation, Task>? completeFunc)\n    {\n        // ReSharper disable once ConvertToLocalFunction\n        // ReSharper disable once ConvertIfStatementToNullCoalescingAssignment\n        if (null == completeFunc)\n        {\n            completeFunc = x =>\n            {\n                if (x.Method != null && !x.Method.IsAbstract)\n                {\n                    if (x.Target == x.ProxyTarget)\n                    {\n                        // https://stackoverflow.com/questions/2323401/how-to-call-base-base-method\n                        var ptr = x.Method.MethodHandle.GetFunctionPointer();\n                        var delegateType = DelegateHelper.GetDelegateType(x.Method);\n                        var @delegate = (Delegate)Guard.NotNull(Activator.CreateInstance(delegateType, x.Target, ptr));\n                        invocation.ReturnValue = @delegate.DynamicInvoke(x.Arguments);\n                    }\n                    else\n                    {\n                        invocation.ReturnValue = x.Method.Invoke(x.Target, x.Arguments);\n                    }\n                }\n\n                if (invocation.ProxyMethod.ReturnType == typeof(void))\n                {\n                    return Task.CompletedTask;\n                }\n                if (invocation.ReturnValue is Task task)\n                {\n                    return task;\n                }\n                if (invocation.ReturnValue is ValueTask valTask)\n                {\n                    return valTask.AsTask();\n                }\n\n                return Task.CompletedTask;\n            };\n        }\n\n        // ReSharper disable once ConvertIfStatementToNullCoalescingAssignment\n        if (null == interceptors)\n        {\n            interceptors = FluentAspects.AspectOptions.InterceptorResolver\n                .ResolveInterceptors(invocation);\n        }\n\n        if (interceptors.Count <= 1 && interceptors[0] is TryInvokeInterceptor)\n        {\n            return completeFunc;\n        }\n\n        var builder = PipelineBuilder.CreateAsync(completeFunc);\n        foreach (var interceptor in interceptors)\n        {\n            builder.Use(interceptor.Invoke);\n        }\n        return builder.Build();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/AspectInvokeException.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\npublic sealed class AspectInvokeException(IInvocation invocation, Exception innerException) : Exception($\"Invoke {invocation.ProxyMethod.Name} exception\", innerException)\n{\n    public IInvocation Invocation { get; } = invocation;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/AttributeInterceptorResolver.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic abstract class AbstractInterceptor : Attribute, IInterceptor\n{\n    public abstract Task Invoke(IInvocation invocation, Func<Task> next);\n}\n\npublic sealed class NoIntercept : Attribute;\n\npublic class AttributeInterceptorResolver : IInterceptorResolver\n{\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public virtual IReadOnlyList<IInterceptor> ResolveInterceptors(IInvocation invocation)\n    {\n        Guard.NotNull(invocation);\n\n        if ((invocation.Method ?? invocation.ProxyMethod).IsDefined(typeof(NoIntercept)))\n        {\n            return Array.Empty<IInterceptor>();\n        }\n\n        var baseType = (invocation.Method ?? invocation.ProxyMethod).DeclaringType;\n        if (baseType?.IsDefined(typeof(NoIntercept)) == true)\n        {\n            return Array.Empty<IInterceptor>();\n        }\n\n        var list = new List<IInterceptor>();\n        var interceptorTypes = new HashSet<Type>();\n\n        // load method interceptor\n        if (invocation.Method != null)\n        {\n            if (invocation.Method.IsDefined(typeof(NoIntercept)))\n            {\n                return Array.Empty<IInterceptor>();\n            }\n\n            foreach (var interceptor in invocation.Method.GetCustomAttributes<AbstractInterceptor>())\n            {\n                if (interceptorTypes.Add(interceptor.GetType()))\n                {\n                    list.Add(interceptor);\n                }\n            }\n        }\n        else\n        {\n            foreach (var interceptor in invocation.ProxyMethod.GetCustomAttributes<AbstractInterceptor>())\n            {\n                if (interceptorTypes.Add(interceptor.GetType()))\n                {\n                    list.Add(interceptor);\n                }\n            }\n        }\n\n        var parameterTypes = invocation.ProxyMethod.GetParameters().Select(x => x.ParameterType).ToArray();\n\n        while (baseType != null)\n        {\n            if (baseType.GetMethod(invocation.ProxyMethod.Name, parameterTypes) != null)\n            {\n                foreach (var interceptor in baseType.GetCustomAttributes<AbstractInterceptor>())\n                {\n                    if (interceptorTypes.Add(interceptor.GetType()))\n                    {\n                        list.Add(interceptor);\n                    }\n                }\n                baseType = baseType.BaseType;\n            }\n            else\n            {\n                break;\n            }\n        }\n\n        foreach (var @interface in invocation.ProxyTarget.GetType().GetImplementedInterfaces())\n        {\n            var interfaceMethod = @interface.GetMethod(invocation.ProxyMethod.Name, parameterTypes);\n            if (null != interfaceMethod)\n            {\n                // interface interceptor\n                foreach (var interceptor in @interface.GetCustomAttributes<AbstractInterceptor>())\n                {\n                    if (interceptorTypes.Add(interceptor.GetType()))\n                    {\n                        list.Add(interceptor);\n                    }\n                }\n                // interface method interceptor\n                foreach (var interceptor in interfaceMethod.GetCustomAttributes<AbstractInterceptor>())\n                {\n                    if (interceptorTypes.Add(interceptor.GetType()))\n                    {\n                        list.Add(interceptor);\n                    }\n                }\n            }\n        }\n\n        return list;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/DefaultProxyFactory.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic sealed class DefaultProxyFactory\n    (IProxyTypeFactory proxyTypeFactory, IServiceProvider? serviceProvider = null) : IProxyFactory\n{\n    public static readonly IProxyFactory Instance = new DefaultProxyFactory(DefaultProxyTypeFactory.Instance);\n    private readonly IServiceProvider _serviceProvider = serviceProvider ?? DependencyResolver.Current;\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public object CreateProxy(Type serviceType, object?[] arguments)\n    {\n        Guard.NotNull(serviceType);\n\n        var proxyType = proxyTypeFactory.CreateProxyType(serviceType);\n        var proxy = _serviceProvider.CreateInstance(proxyType, arguments);\n        return proxy;\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public object CreateProxy(Type serviceType, Type implementType, params object?[] arguments)\n    {\n        Guard.NotNull(serviceType);\n        Guard.NotNull(implementType);\n\n        var proxyType = proxyTypeFactory.CreateProxyType(serviceType, implementType);\n        if (serviceType.IsInterface)\n        {\n            var implement = _serviceProvider.CreateInstance(implementType, arguments);\n            var proxy = _serviceProvider.CreateInstance(proxyType);\n            ProxyUtils.SetProxyTarget(proxy, implement);\n            return proxy;\n        }\n\n        return _serviceProvider.CreateInstance(proxyType, arguments);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public object CreateProxyWithTarget(Type serviceType, object implement, object?[] arguments)\n    {\n        Guard.NotNull(serviceType);\n        Guard.NotNull(implement);\n\n        var implementType = implement.GetType();\n\n        var proxyType = serviceType.IsClass\n                ? proxyTypeFactory.CreateProxyType(serviceType)\n                : proxyTypeFactory.CreateProxyType(serviceType, implementType)\n            ;\n        var proxy = _serviceProvider.CreateInstance(proxyType, arguments);\n        ProxyUtils.SetProxyTarget(proxy, implement);\n\n        return proxy;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/DefaultProxyTypeFactory.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\npublic sealed class DefaultProxyTypeFactory : IProxyTypeFactory\n{\n    public static readonly IProxyTypeFactory Instance = new DefaultProxyTypeFactory();\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public Type CreateProxyType(Type serviceType)\n    {\n        Guard.NotNull(serviceType);\n\n        if (serviceType.IsInterface)\n        {\n            return ProxyUtils.CreateInterfaceProxy(serviceType);\n        }\n        return ProxyUtils.CreateClassProxy(serviceType, serviceType);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public Type CreateProxyType(Type serviceType, Type implementType)\n    {\n        Guard.NotNull(serviceType);\n\n        if (serviceType.IsInterface)\n        {\n            return ProxyUtils.CreateInterfaceProxy(serviceType, implementType);\n        }\n\n        return ProxyUtils.CreateClassProxy(serviceType, implementType);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/DelegateInterceptor.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\n[CLSCompliant(false)]\npublic sealed class DelegateInterceptor(Func<IInvocation, Func<Task>, Task> interceptFunc) : AbstractInterceptor\n{\n    private readonly Func<IInvocation, Func<Task>, Task> _interceptFunc = Guard.NotNull(interceptFunc);\n\n    public override Task Invoke(IInvocation invocation, Func<Task> next)\n    {\n        return _interceptFunc.Invoke(invocation, next);\n    }\n\n    public static DelegateInterceptor FromDelegate(Func<IInvocation, Func<Task>, Task> interceptFunc) => new(interceptFunc);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/FluentAspectOptions.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\npublic sealed class FluentAspectOptions\n{\n    public readonly Dictionary<Func<IInvocation, bool>, IInterceptionConfiguration> InterceptionConfigurations = [];\n    private IInterceptorResolver _interceptorResolver = FluentConfigInterceptorResolver.Instance;\n\n    public HashSet<Func<IInvocation, bool>> NoInterceptionConfigurations { get; } = [];\n\n    public IInterceptorResolver InterceptorResolver\n    {\n        get => _interceptorResolver;\n        set => _interceptorResolver = Guard.NotNull(value);\n    }\n\n    public HashSet<IInvocationEnricher> Enrichers { get; } = [];\n\n    public IProxyFactory ProxyFactory { get; set; } = DefaultProxyFactory.Instance;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/FluentAspectOptionsExtensions.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic static class FluentAspectOptionsExtensions\n{\n    #region Intercept\n\n    public static IInterceptionConfiguration Intercept(this FluentAspectOptions options,\n        Func<IInvocation, bool> predict)\n    {\n        Guard.NotNull(predict);\n\n        if (options.InterceptionConfigurations.TryGetValue\n            (predict, out var interceptionConfiguration))\n        {\n            return interceptionConfiguration;\n        }\n\n        interceptionConfiguration = new InterceptionConfiguration();\n        options.InterceptionConfigurations[predict] = interceptionConfiguration;\n        return interceptionConfiguration;\n    }\n\n    public static IInterceptionConfiguration InterceptAll(this FluentAspectOptions options)\n        => options.Intercept(_ => true);\n\n    public static IInterceptionConfiguration InterceptType(this FluentAspectOptions options,\n        Func<Type, bool> typesFilter)\n    {\n        Guard.NotNull(typesFilter);\n\n        return options.InterceptMethod(m => typesFilter(m.DeclaringType!));\n    }\n\n    public static IInterceptionConfiguration InterceptMethod<T>(this FluentAspectOptions options,\n        Expression<Func<T, object>> method)\n    {\n        return options.InterceptMethod<T>(method.GetMethodExpression());\n    }\n\n    public static IInterceptionConfiguration InterceptMethod<T>(this FluentAspectOptions options,\n        MethodInfo method)\n    {\n        Guard.NotNull(method);\n\n        var methodSignature = method.GetSignature();\n        return options.InterceptMethod<T>(m => m.GetSignature().Equals(methodSignature));\n    }\n\n    public static IInterceptionConfiguration InterceptMethod(this FluentAspectOptions options,\n        MethodInfo method)\n    {\n        Guard.NotNull(method);\n\n        var methodSignature = method.GetSignature();\n        return options.InterceptMethod(m => m.GetSignature().Equals(methodSignature));\n    }\n\n    public static IInterceptionConfiguration InterceptPropertyGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,\n        Expression<Func<T, object>> expression)\n    {\n        var prop = expression.GetProperty();\n        if (null == prop)\n        {\n            throw new InvalidOperationException(\"no property found\");\n        }\n\n        if (!prop.CanRead || prop.GetMethod == null)\n        {\n            throw new InvalidOperationException($\"the property {prop.Name} can not read\");\n        }\n\n        return options.InterceptMethod<T>(prop.GetMethod);\n    }\n\n    public static IInterceptionConfiguration InterceptPropertySetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,\n        Expression<Func<T, object>> expression)\n    {\n        var prop = expression.GetProperty();\n        if (null == prop)\n        {\n            throw new InvalidOperationException(\"no property found\");\n        }\n\n        if (!prop.CanWrite || prop.SetMethod == null)\n        {\n            throw new InvalidOperationException($\"the property {prop.Name} can not write\");\n        }\n\n        return options.InterceptMethod<T>(prop.SetMethod);\n    }\n\n    public static IInterceptionConfiguration InterceptMethod<T>(this FluentAspectOptions options,\n        Expression<Action<T>> method)\n    {\n        return options.InterceptMethod<T>(method.GetMethodExpression());\n    }\n\n    public static IInterceptionConfiguration InterceptType<T>(this FluentAspectOptions options)\n    {\n        return options.InterceptMethod(m => m.DeclaringType!.IsAssignableTo<T>());\n    }\n\n    public static IInterceptionConfiguration InterceptMethod<T>(this FluentAspectOptions options,\n        Expression<Func<MethodInfo, bool>> andExpression)\n    {\n        Expression<Func<MethodInfo, bool>> expression = m => m.DeclaringType!.IsAssignableTo<T>();\n        expression = expression.And(Guard.NotNull(andExpression, nameof(andExpression)));\n        return options.InterceptMethod(expression.Compile());\n    }\n\n    public static IInterceptionConfiguration InterceptMethod(this FluentAspectOptions options,\n        Func<MethodInfo, bool> methodPredict)\n    {\n        return options.Intercept(invocation => methodPredict(invocation.Method ?? invocation.ProxyMethod));\n    }\n\n    public static IInterceptionConfiguration InterceptMethod<T>(this FluentAspectOptions options,\n        MethodCallExpression methodCallExpression)\n    {\n        var innerMethod = methodCallExpression.Method;\n        if (null == innerMethod)\n        {\n            throw new InvalidOperationException(\"no method found\");\n        }\n\n        Expression<Func<MethodInfo, bool>> expression = m => m.DeclaringType!.IsAssignableTo<T>();\n        var methodSignature = innerMethod.GetSignature();\n        expression = expression.And(m => m.GetSignature().Equals(methodSignature));\n        return options.InterceptMethod(expression.Compile());\n    }\n\n    #endregion Intercept\n\n    #region NoIntercept\n\n    public static bool NoIntercept(this FluentAspectOptions options, Func<IInvocation, bool> predict)\n    {\n        return options.NoInterceptionConfigurations.Add(predict);\n    }\n\n    public static FluentAspectOptions NoInterceptMethod<T>(this FluentAspectOptions options,\n        Expression<Func<T, object>> method)\n    {\n        options.NoInterceptMethod<T>(method.GetMethodExpression());\n        return options;\n    }\n\n    public static FluentAspectOptions NoInterceptMethod<T>(this FluentAspectOptions options,\n        Expression<Action<T>> method)\n    {\n        options.NoInterceptMethod<T>(method.GetMethodExpression());\n        return options;\n    }\n\n    public static FluentAspectOptions NoInterceptType(this FluentAspectOptions options,\n        Func<Type, bool> typesFilter)\n    {\n        Guard.NotNull(options, nameof(options));\n        Guard.NotNull(typesFilter, nameof(typesFilter));\n        options.NoInterceptMethod(m => typesFilter(m.DeclaringType!));\n        return options;\n    }\n\n    public static FluentAspectOptions NoInterceptType<T>(this FluentAspectOptions options)\n    {\n        options.NoInterceptMethod(m => m.DeclaringType!.IsAssignableTo<T>());\n        return options;\n    }\n\n    public static FluentAspectOptions NoInterceptMethod<T>(this FluentAspectOptions options,\n        Expression<Func<MethodInfo, bool>> andExpression)\n    {\n        Expression<Func<MethodInfo, bool>> expression = m => m.DeclaringType!.IsAssignableTo<T>();\n        expression = expression.And(Guard.NotNull(andExpression, nameof(andExpression)));\n        options.NoInterceptMethod(expression.Compile());\n        return options;\n    }\n\n    public static FluentAspectOptions NoInterceptMethod<T>(this FluentAspectOptions options,\n        MethodCallExpression methodCallExpression)\n    {\n        var innerMethod = methodCallExpression.Method;\n        if (null == innerMethod)\n        {\n            throw new InvalidOperationException($\"no method found\");\n        }\n\n        Expression<Func<MethodInfo, bool>> expression = m => m.DeclaringType!.IsAssignableTo<T>();\n        var methodSignature = innerMethod.GetSignature();\n        expression = expression.And(m => m.GetSignature().Equals(methodSignature));\n        return options.NoInterceptMethod(expression.Compile());\n    }\n\n    public static FluentAspectOptions NoInterceptMethod(this FluentAspectOptions options,\n        Func<MethodInfo, bool> methodPredict)\n    {\n        options.NoIntercept(invocation => methodPredict(invocation.Method ?? invocation.ProxyMethod));\n        return options;\n    }\n\n    public static FluentAspectOptions NoInterceptMethod<T>(this FluentAspectOptions options,\n        MethodInfo method)\n    {\n        Guard.NotNull(method);\n\n        var methodSignature = method.GetSignature();\n        return options.NoInterceptMethod<T>(m => m.GetSignature().Equals(methodSignature));\n    }\n\n    public static FluentAspectOptions NoInterceptMethod(this FluentAspectOptions options,\n        MethodInfo method)\n    {\n        Guard.NotNull(method);\n\n        var methodSignature = method.GetSignature();\n        return options.NoInterceptMethod(m => m.GetSignature().Equals(methodSignature));\n    }\n\n    public static FluentAspectOptions NoInterceptProperty<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,\n        Expression<Func<T, object>> expression)\n    {\n        var prop = expression.GetProperty();\n        if (null == prop)\n        {\n            throw new InvalidOperationException(\"no property found\");\n        }\n\n        if (prop.GetMethod != null)\n        {\n            options = options.NoInterceptMethod<T>(prop.GetMethod);\n        }\n        if (prop.SetMethod != null)\n        {\n            options = options.NoInterceptMethod<T>(prop.SetMethod);\n        }\n        return options;\n    }\n\n    public static FluentAspectOptions NoInterceptPropertyGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,\n        Expression<Func<T, object>> expression)\n    {\n        var prop = expression.GetProperty();\n        if (null == prop)\n        {\n            throw new InvalidOperationException(\"no property found\");\n        }\n\n        if (!prop.CanRead || prop.GetMethod == null)\n        {\n            throw new InvalidOperationException($\"the property {prop.Name} can not read\");\n        }\n\n        return options.NoInterceptMethod<T>(prop.GetMethod);\n    }\n\n    public static FluentAspectOptions NoInterceptPropertySetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,\n        Expression<Func<T, object>> expression)\n    {\n        var prop = expression.GetProperty();\n        if (null == prop)\n        {\n            throw new InvalidOperationException(\"no property found\");\n        }\n\n        if (!prop.CanWrite || prop.SetMethod == null)\n        {\n            throw new InvalidOperationException($\"the property {prop.Name} can not write\");\n        }\n\n        return options.NoInterceptMethod<T>(prop.SetMethod);\n    }\n\n    #endregion NoIntercept\n\n    #region InterceptorResolver\n\n    public static FluentAspectOptions UseInterceptorResolver(this FluentAspectOptions options,\n        IInterceptorResolver resolver)\n    {\n        Guard.NotNull(resolver);\n        options.InterceptorResolver = resolver;\n        return options;\n    }\n\n    public static FluentAspectOptions UseInterceptorResolver<TResolver>(this FluentAspectOptions options)\n        where TResolver : IInterceptorResolver, new()\n    {\n        options.InterceptorResolver = new TResolver();\n        return options;\n    }\n\n    #endregion InterceptorResolver\n\n    #region ProxyFactory\n\n    public static FluentAspectOptions UseProxyFactory(this FluentAspectOptions options, IProxyFactory proxyFactory)\n    {\n        options.ProxyFactory = proxyFactory;\n        return options;\n    }\n\n    public static FluentAspectOptions UseProxyFactory<TProxyFactory>(this FluentAspectOptions options)\n        where TProxyFactory : class, IProxyFactory, new()\n    {\n        options.ProxyFactory = new TProxyFactory();\n        return options;\n    }\n\n    public static FluentAspectOptions UseProxyFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TProxyFactory>(this FluentAspectOptions options,\n        params object[] parameters) where TProxyFactory : class, IProxyFactory\n    {\n        options.ProxyFactory = ActivatorHelper.CreateInstance<TProxyFactory>(parameters);\n        return options;\n    }\n\n    #endregion ProxyFactory\n\n    #region Enricher\n\n    public static FluentAspectOptions WithProperty(this FluentAspectOptions options, string propertyName,\n        object propertyValue, bool overwrite = false)\n    {\n        options.Enrichers.Add(new PropertyInvocationEnricher(propertyName, propertyValue, overwrite));\n        return options;\n    }\n\n    public static FluentAspectOptions WithProperty(this FluentAspectOptions options, string propertyName,\n        Func<IInvocation, object> propertyValueFactory, bool overwrite = false)\n    {\n        options.Enrichers.Add(new PropertyInvocationEnricher(propertyName, propertyValueFactory, overwrite));\n        return options;\n    }\n\n    public static FluentAspectOptions WithEnricher<TEnricher>(this FluentAspectOptions options) where TEnricher : IInvocationEnricher, new()\n    {\n        options.Enrichers.Add(new TEnricher());\n        return options;\n    }\n\n    public static FluentAspectOptions WithEnricher<TEnricher>(this FluentAspectOptions options, TEnricher enricher) where TEnricher : IInvocationEnricher\n    {\n        options.Enrichers.Add(enricher);\n        return options;\n    }\n\n    #endregion Enricher\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/FluentAspects.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\npublic static class FluentAspects\n{\n    public static readonly FluentAspectOptions AspectOptions;\n\n    static FluentAspects()\n    {\n        AspectOptions = new FluentAspectOptions();\n        // register built-in ignore interceptors\n        AspectOptions\n            .NoInterceptType(t => t.Namespace?.StartsWith(\"WeihanLi.Common.Aspect\") == true)\n            ;\n        // register built-in necessary interceptors\n        AspectOptions.InterceptAll()\n            .With<TryInvokeInterceptor>();\n        AspectOptions.InterceptMethod<IDisposable>(m => m.Dispose())\n            .With<DisposableInterceptor>();\n    }\n\n    public static void Configure(Action<FluentAspectOptions> optionsAction)\n    {\n        Guard.NotNull(optionsAction, nameof(optionsAction)).Invoke(AspectOptions);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/FluentAspectsBuilder.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic interface IFluentAspectsBuilder\n{\n    IServiceCollection Services { get; }\n}\n\ninternal sealed class FluentAspectsBuilder(IServiceCollection serviceCollection) : IFluentAspectsBuilder\n{\n    public IServiceCollection Services { get; } = serviceCollection;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/FluentAspectsServiceContainerBuilder.cs",
    "content": "﻿using WeihanLi.Common.DependencyInjection;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic interface IFluentAspectsServiceContainerBuilder\n{\n    IServiceContainerBuilder Services { get; }\n}\n\ninternal sealed class FluentAspectsServiceContainerBuilder(IServiceContainerBuilder serviceCollection) : IFluentAspectsServiceContainerBuilder\n{\n    public IServiceContainerBuilder Services { get; } = serviceCollection;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/FluentConfigInterceptorResolver.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\npublic sealed class FluentConfigInterceptorResolver : IInterceptorResolver\n{\n    public static readonly IInterceptorResolver Instance = new FluentConfigInterceptorResolver();\n\n    private FluentConfigInterceptorResolver()\n    {\n    }\n\n    public IReadOnlyList<IInterceptor> ResolveInterceptors(IInvocation invocation)\n    {\n        foreach (var func in FluentAspects.AspectOptions.NoInterceptionConfigurations)\n        {\n            if (func(invocation))\n            {\n                return Array.Empty<IInterceptor>();\n            }\n        }\n        var interceptorTypes = new HashSet<Type>();\n        var interceptors = new List<IInterceptor>();\n        foreach (var configuration in FluentAspects.AspectOptions.InterceptionConfigurations)\n        {\n            if (configuration.Key.Invoke(invocation))\n            {\n                foreach (var interceptor in configuration.Value.Interceptors)\n                {\n                    if (interceptorTypes.Add(interceptor.GetType()))\n                    {\n                        interceptors.Add(interceptor);\n                    }\n                }\n            }\n        }\n        return interceptors;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/IInterceptionConfiguration.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic interface IInterceptionConfiguration\n{\n    ICollection<IInterceptor> Interceptors { get; }\n}\n\ninternal class InterceptionConfiguration : IInterceptionConfiguration\n{\n    public ICollection<IInterceptor> Interceptors { get; }\n\n    public InterceptionConfiguration()\n    {\n        Interceptors = new List<IInterceptor>();\n    }\n}\n\npublic static class InterceptionConfigurationExtensions\n{\n    public static IInterceptionConfiguration With(this IInterceptionConfiguration interceptionConfiguration, Func<IInvocation, Func<Task>, Task> interceptFunc)\n    {\n        Guard.NotNull(interceptionConfiguration, nameof(interceptionConfiguration))\n            .Interceptors.Add(new DelegateInterceptor(interceptFunc));\n\n        return interceptionConfiguration;\n    }\n\n    public static IInterceptionConfiguration With(this IInterceptionConfiguration interceptionConfiguration, IInterceptor interceptor)\n    {\n        interceptionConfiguration.Interceptors.Add(interceptor);\n        return interceptionConfiguration;\n    }\n\n    public static IInterceptionConfiguration With<TInterceptor>(this IInterceptionConfiguration interceptionConfiguration) where TInterceptor : IInterceptor, new()\n    {\n        interceptionConfiguration.With(new TInterceptor());\n        return interceptionConfiguration;\n    }\n\n    public static IInterceptionConfiguration With<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TInterceptor>(this IInterceptionConfiguration interceptionConfiguration, params object?[] parameters) where TInterceptor : IInterceptor\n    {\n        if (Guard.NotNull(parameters, nameof(parameters)).Length == 0)\n        {\n            interceptionConfiguration.With(NewFuncHelper<TInterceptor>.Instance());\n        }\n        else\n        {\n            interceptionConfiguration.With(ActivatorHelper.CreateInstance<TInterceptor>(parameters));\n        }\n        return interceptionConfiguration;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/IInterceptor.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\npublic interface IInterceptor\n{\n    Task Invoke(IInvocation invocation, Func<Task> next);\n}\n\npublic class TryInvokeInterceptor : IInterceptor\n{\n    public async Task Invoke(IInvocation invocation, Func<Task> next)\n    {\n        try\n        {\n            await next();\n        }\n        catch (Exception e)\n        {\n            throw new AspectInvokeException(invocation, e);\n        }\n    }\n}\n\ninternal sealed class DisposableInterceptor : IInterceptor\n{\n    public async Task Invoke(IInvocation invocation, Func<Task> next)\n    {\n        await next();\n        // avoid cycling call\n        // ReSharper disable once ConditionIsAlwaysTrueOrFalse\n        if (invocation.Target != null\n            && invocation.Target != invocation.ProxyTarget\n            && invocation.ProxyMethod.Name == nameof(IDisposable.Dispose)\n            && invocation.Target is IDisposable disposable\n        )\n        {\n            disposable.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/IInterceptorResolver.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\npublic interface IInterceptorResolver\n{\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    IReadOnlyList<IInterceptor> ResolveInterceptors(IInvocation invocation);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/IInvocation.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic interface IInvocation\n{\n    MethodInfo ProxyMethod { get; }\n\n    object ProxyTarget { get; }\n\n    MethodInfo? Method { get; }\n\n    object? Target { get; }\n\n    object[] Arguments { get; }\n\n    Type[] GenericArguments { get; }\n\n    object? ReturnValue { get; set; }\n\n    Dictionary<string, object?> Properties { get; }\n}\n\npublic class AspectInvocation : IInvocation\n{\n    public MethodInfo ProxyMethod { get; }\n\n    public object ProxyTarget { get; }\n\n    public MethodInfo? Method { get; }\n\n    public object? Target { get; }\n\n    public object[] Arguments { get; }\n\n    public Type[] GenericArguments { get; }\n\n    public object? ReturnValue { get; set; }\n\n    public Dictionary<string, object?> Properties { get; }\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    public AspectInvocation(\n        MethodInfo proxyMethod,\n        MethodInfo? methodBase,\n        object proxyTarget,\n        object? target,\n        object[] arguments)\n    {\n        Method = methodBase;\n        ProxyTarget = proxyTarget;\n        Target = target;\n        Arguments = arguments;\n        GenericArguments = methodBase?.GetGenericArguments() ?? [];\n\n        if (proxyMethod.ContainsGenericParameters && GenericArguments.Length > 0)\n        {\n            ProxyMethod = proxyMethod.MakeGenericMethod(GenericArguments);\n        }\n        else\n        {\n            ProxyMethod = proxyMethod;\n        }\n\n        Properties = [];\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/IProxyFactory.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic interface IProxyFactory\n{\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    object CreateProxy(Type serviceType, object?[] arguments);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    object CreateProxy(Type serviceType, Type implementType, params object?[] arguments);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    object CreateProxyWithTarget(Type serviceType, object implement, object?[] arguments);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/IProxyTypeFactory.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\npublic interface IProxyTypeFactory\n{\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    Type CreateProxyType(Type serviceType);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    Type CreateProxyType(Type serviceType, Type implementType);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/InternalAspectHelper.cs",
    "content": "﻿using System.Reflection;\n\nnamespace WeihanLi.Common.Aspect;\n\ninternal static class MethodInvokeHelper\n{\n    public static readonly MethodInfo GetInvocationReturnValueMethod =\n        typeof(AspectInvocation).GetProperty(\"ReturnValue\")!.GetGetMethod()!;\n\n    [UnconditionalSuppressMessage(\"Trimming\", \"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code\", Justification = \"<Pending>\")]\n    public static readonly MethodInfo InvokeAspectDelegateMethod =\n        typeof(AspectDelegate).GetMethod(nameof(AspectDelegate.Invoke))!;\n\n    [UnconditionalSuppressMessage(\"Trimming\", \"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code\", Justification = \"<Pending>\")]\n    public static readonly MethodInfo GetCurrentMethod =\n        typeof(MethodBase).GetMethod(nameof(MethodBase.GetCurrentMethod))!;\n\n    [UnconditionalSuppressMessage(\"Trimming\", \"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code\", Justification = \"<Pending>\")]\n    public static readonly ConstructorInfo AspectInvocationConstructor =\n        typeof(AspectInvocation).GetConstructors()[0];\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/InvocationEnricher.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic interface IInvocationEnricher : IEnricher<IInvocation>;\n\npublic sealed class PropertyInvocationEnricher : PropertyEnricher<IInvocation>, IInvocationEnricher\n{\n    public PropertyInvocationEnricher(string propertyName, object propertyValue, bool overwrite = false) : base(propertyName, propertyValue, overwrite)\n    {\n    }\n\n    public PropertyInvocationEnricher(string propertyName, Func<IInvocation, object> propertyValueFactory, bool overwrite = false) : base(propertyName, propertyValueFactory, overwrite)\n    {\n    }\n\n    public PropertyInvocationEnricher(string propertyName, Func<IInvocation, object> propertyValueFactory, Func<IInvocation, bool> propertyPredict, bool overwrite = false) : base(propertyName, propertyValueFactory, propertyPredict, overwrite)\n    {\n    }\n\n    protected override Action<IInvocation, string, Func<IInvocation, object>, bool> EnrichAction =>\n        (invocation, propertyName, propertyValueFactory, overwrite) =>\n            invocation.AddProperty(propertyName, propertyValueFactory, overwrite);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/InvocationEnricherExtensions.cs",
    "content": "﻿namespace WeihanLi.Common.Aspect;\n\npublic static class InvocationEnricherExtensions\n{\n    public static void AddProperty(this IInvocation invocation, string propertyName,\n        object propertyValue, bool overwrite = false)\n    {\n        Guard.NotNull(invocation);\n\n        if (!invocation.Properties.ContainsKey(propertyName) || overwrite)\n        {\n            invocation.Properties[propertyName] = propertyValue;\n        }\n    }\n\n    public static void AddProperty(this IInvocation invocation, string propertyName,\n        Func<IInvocation, object?> propertyValueFactory, bool overwrite = false)\n    {\n        Guard.NotNull(invocation);\n\n        if (!invocation.Properties.ContainsKey(propertyName)\n            || overwrite)\n        {\n            invocation.Properties[propertyName]\n                = Guard.NotNull(propertyValueFactory, nameof(propertyValueFactory)).Invoke(invocation);\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/MethodSignature.cs",
    "content": "﻿using System.Reflection;\n\nnamespace WeihanLi.Common.Aspect;\n\ninternal sealed class MethodSignature(string methodName, IReadOnlyList<Type> parameters)\n{\n    public IReadOnlyList<Type> Parameters { get; } = parameters;\n    public string MethodName { get; } = methodName;\n\n    public MethodSignature(MethodBase method) : this(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray())\n    {\n    }\n\n    public override bool Equals(object? obj)\n    {\n        // if object is null or type does not match return false...\n        if (obj is not MethodSignature signature)\n            return false;\n\n        // compares method name...\n        if (MethodName != signature.MethodName)\n            return false;\n\n        // compares params ...\n        if (Parameters.Count != signature.Parameters.Count)\n            return false;\n\n        // compares params types...\n        for (var i = 0; i < Parameters.Count; i++)\n        {\n            if (!Parameters[i].IsGenericParameter && Parameters[i] != signature.Parameters[i])\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public override int GetHashCode()\n    {\n        // XORs members...\n        var hash = MethodName.GetHashCode();\n        return Parameters.Aggregate(hash, (a, b) => a ^ b.GetHashCode());\n    }\n}\n\ninternal static class MethodSignatureExtensions\n{\n    public static MethodSignature GetSignature(this MethodBase method)\n    {\n        return new(method);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/ProxyFactoryExtensions.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic static class ProxyFactoryExtensions\n{\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TInterface CreateInterfaceProxy<TInterface>(this IProxyFactory proxyGenerator)\n        where TInterface : class\n    {\n        var type = typeof(TInterface);\n        if (!type.IsInterface)\n        {\n            throw new InvalidOperationException($\"the Type {type.FullName} is not interface\");\n        }\n        return proxyGenerator.CreateProxy<TInterface>();\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TInterface CreateInterfaceProxy<TInterface, TImplement>(this IProxyFactory proxyGenerator, params object?[] arguments) where TImplement : TInterface\n        where TInterface : class\n    {\n        var type = typeof(TInterface);\n        if (!type.IsInterface)\n        {\n            throw new InvalidOperationException($\"the Type {type.FullName} is not interface\");\n        }\n        return proxyGenerator.CreateProxy<TInterface, TImplement>(arguments);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TInterface CreateInterfaceProxy<TInterface, TImplement>(this IProxyFactory proxyGenerator, TImplement implement) where TImplement : TInterface\n        where TInterface : class\n    {\n        var type = typeof(TInterface);\n        if (!type.IsInterface)\n        {\n            throw new InvalidOperationException($\"the Type {type.FullName} is not interface\");\n        }\n        return proxyGenerator.CreateProxyWithTarget<TInterface>(implement);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TClass CreateClassProxy<TClass>(this IProxyFactory proxyGenerator, params object?[] arguments) where TClass : class\n    {\n        var type = typeof(TClass);\n        if (!type.IsClass)\n        {\n            throw new InvalidOperationException($\"the Type {type.FullName} is not class\");\n        }\n        return proxyGenerator.CreateProxy<TClass>(arguments);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TClass CreateClassProxy<TClass, TImplement>(this IProxyFactory proxyGenerator, params object?[] arguments) where TImplement : TClass\n        where TClass : class\n    {\n        var type = typeof(TClass);\n        if (!type.IsClass)\n        {\n            throw new InvalidOperationException($\"the Type {type.FullName} is not class\");\n        }\n        return proxyGenerator.CreateProxy<TClass, TImplement>(arguments);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TClass CreateClassProxy<TClass, TImplement>(this IProxyFactory proxyGenerator, TImplement implement) where TImplement : TClass\n        where TClass : class\n    {\n        var type = typeof(TClass);\n        if (!type.IsClass)\n        {\n            throw new InvalidOperationException($\"the Type {type.FullName} is not class\");\n        }\n        return proxyGenerator.CreateProxyWithTarget<TClass>(implement);\n    }\n\n    #region CreateProxy\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static object CreateProxy(this IProxyFactory proxyFactory, Type serviceType, params object?[] arguments)\n    {\n        return proxyFactory.CreateProxy(serviceType, arguments);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TService CreateProxy<TService>(this IProxyFactory proxyFactory, params object?[] arguments) where TService : class\n    {\n        return (TService)proxyFactory.CreateProxy(typeof(TService), arguments);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TService CreateProxy<TService, TImplement>(this IProxyFactory proxyFactory, params object?[] arguments)\n        where TImplement : TService\n        where TService : class\n    {\n        return (TService)proxyFactory.CreateProxy(typeof(TService), typeof(TImplement), arguments);\n    }\n\n    #endregion CreateProxy\n\n    #region CreateProxyWithTarget\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TService CreateProxyWithTarget<TService, TImplement>(this IProxyFactory proxyFactory, TImplement target)\n        where TImplement : TService\n        where TService : class\n    {\n        return (TService)proxyFactory.CreateProxyWithTarget(typeof(TService), target);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TService CreateProxyWithTarget<TService>(this IProxyFactory proxyFactory, object target)\n        where TService : class\n    {\n        return (TService)proxyFactory.CreateProxyWithTarget(typeof(TService), target);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static TService CreateProxyWithTarget<TService>(this IProxyFactory proxyFactory, object target, object[] arguments)\n        where TService : class\n    {\n        return (TService)proxyFactory.CreateProxyWithTarget(typeof(TService), target, arguments);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static object CreateProxyWithTarget(this IProxyFactory proxyFactory, Type serviceType, object target)\n    {\n        return proxyFactory.CreateProxyWithTarget(serviceType, target, []);\n    }\n\n    #endregion CreateProxyWithTarget\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/ProxyUtils.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing System.Reflection.Emit;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Aspect;\n\n[RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\ninternal static class ProxyUtils\n{\n    public const string ProxyAssemblyName = \"FluentAspects.DynamicGenerated\";\n\n    private const MethodAttributes OverrideMethodAttributes = MethodAttributes.HideBySig | MethodAttributes.Virtual;\n    private const MethodAttributes InterfaceMethodAttributes = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual;\n\n    private static readonly ModuleBuilder _moduleBuilder;\n    private static readonly Dictionary<string, Type> _proxyTypes = [];\n\n    private const string TargetFieldName = \"__target\";\n    private static readonly Lock _typeLock = new();\n\n    private static readonly Func<Type, Type?, string> _proxyTypeNameResolver;\n\n    private static readonly HashSet<string> _ignoredMethods =\n    [\n        \"ToString\",\n        \"GetHashCode\",\n        \"Equals\",\n        \"GetType\",\n        \"Finalize\",\n    ];\n\n    static ProxyUtils()\n    {\n        var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(ProxyAssemblyName), AssemblyBuilderAccess.Run);\n        _moduleBuilder = asmBuilder.DefineDynamicModule(\"Default\");\n\n        _proxyTypeNameResolver = (serviceType, implementType) =>\n        {\n            var typeName1 = serviceType.GetFriendlyTypeName();\n            var typeName2 = implementType?.GetFriendlyTypeName();\n\n            return $\"{ProxyAssemblyName}.{typeName1}.{typeName2}\".TrimEnd('.');\n        };\n    }\n\n    public static bool IsProxyType(this Type type)\n    {\n        return Guard.NotNull(type, nameof(type)).FullName?.StartsWith(ProxyAssemblyName) == true && type.Assembly.IsDynamic;\n    }\n\n    private static string GetFriendlyTypeName(this Type? type)\n    {\n        if (type is null)\n            return string.Empty;\n\n        if (type.IsGenericType && !type.IsGenericTypeDefinition)\n        {\n            var typeName = type.FullName ?? type.Name;\n            var genericArgumentTypes = type.GetGenericArguments();\n            foreach (var genericArgumentType in genericArgumentTypes)\n            {\n                if (genericArgumentType.IsBasicType() && !string.IsNullOrEmpty(genericArgumentType.FullName))\n                {\n                    typeName = typeName.Replace(genericArgumentType.FullName ?? genericArgumentType.Name, genericArgumentType.Name);\n                }\n            }\n            return typeName;\n        }\n\n        return type.IsBasicType() ? type.Name : type.FullName ?? type.Name;\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static Type CreateInterfaceProxy(Type interfaceType)\n    {\n        Guard.NotNull(interfaceType);\n        if (!interfaceType.IsInterface)\n        {\n            throw new InvalidOperationException($\"{interfaceType.FullName} is not an interface\");\n        }\n\n        var proxyTypeName = _proxyTypeNameResolver(interfaceType, null);\n\n        // ReSharper disable once InconsistentlySynchronizedField\n        if (_proxyTypes.TryGetValue(proxyTypeName, out var proxyType))\n        {\n            return proxyType;\n        }\n\n        lock (_typeLock)\n        {\n            if (_proxyTypes.TryGetValue(proxyTypeName, out proxyType))\n            {\n                return proxyType;\n            }\n\n            var typeBuilder = _moduleBuilder.DefineType(proxyTypeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed, typeof(object), [interfaceType]);\n\n            GenericParameterUtils.DefineGenericParameter(interfaceType, typeBuilder);\n\n            // define default constructor\n            typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);\n\n            // properties\n            var propertyMethods = new HashSet<string>();\n\n            var properties = interfaceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);\n            foreach (var property in properties)\n            {\n                var propertyBuilder = typeBuilder.DefineProperty(property.Name, property.Attributes, property.PropertyType, Type.EmptyTypes);\n                var field = typeBuilder.DefineField($\"_{property.Name}\", property.PropertyType, FieldAttributes.Private);\n                if (property.CanRead)\n                {\n                    var methodBuilder = typeBuilder.DefineMethod(property.GetMethod!.Name, InterfaceMethodAttributes, property.GetMethod.CallingConvention, property.GetMethod.ReturnType, property.GetMethod.GetParameters().Select(p => p.ParameterType).ToArray());\n                    var ilGen = methodBuilder.GetILGenerator();\n                    ilGen.Emit(OpCodes.Ldarg_0);\n                    ilGen.Emit(OpCodes.Ldfld, field);\n                    ilGen.Emit(OpCodes.Ret);\n                    typeBuilder.DefineMethodOverride(methodBuilder, property.GetMethod);\n                    propertyBuilder.SetGetMethod(methodBuilder);\n                    propertyMethods.Add(property.GetMethod.Name);\n                }\n                if (property.CanWrite)\n                {\n                    var methodBuilder = typeBuilder.DefineMethod(property.SetMethod!.Name, InterfaceMethodAttributes, property.SetMethod.CallingConvention, property.SetMethod.ReturnType, property.SetMethod.GetParameters().Select(p => p.ParameterType).ToArray());\n                    var ilGen = methodBuilder.GetILGenerator();\n                    ilGen.Emit(OpCodes.Ldarg_0);\n                    ilGen.Emit(OpCodes.Ldarg_1);\n                    ilGen.Emit(OpCodes.Stfld, field);\n                    ilGen.Emit(OpCodes.Ret);\n                    typeBuilder.DefineMethodOverride(methodBuilder, property.SetMethod);\n                    propertyBuilder.SetSetMethod(methodBuilder);\n                    propertyMethods.Add(property.SetMethod.Name);\n                }\n                foreach (var customAttributeData in property.CustomAttributes)\n                {\n                    propertyBuilder.SetCustomAttribute(DefineCustomAttribute(customAttributeData));\n                }\n            }\n\n            // methods\n            var methods = interfaceType.GetMethods(BindingFlags.Instance | BindingFlags.Public);\n            foreach (var method in methods.Where(x => !propertyMethods.Contains(x.Name) && !_ignoredMethods.Contains(x.Name)))\n            {\n                MethodUtils.DefineInterfaceMethod(typeBuilder, method, null);\n            }\n\n            foreach (var implementedInterface in interfaceType.GetImplementedInterfaces())\n            {\n                properties = implementedInterface.GetProperties(BindingFlags.Instance | BindingFlags.Public);\n                foreach (var property in properties)\n                {\n                    var propertyBuilder = typeBuilder.DefineProperty(property.Name, property.Attributes, property.PropertyType, Type.EmptyTypes);\n                    var field = typeBuilder.DefineField($\"_{property.Name}\", property.PropertyType, FieldAttributes.Private);\n                    if (property.CanRead)\n                    {\n                        var methodBuilder = typeBuilder.DefineMethod(property.GetMethod!.Name, InterfaceMethodAttributes, property.GetMethod.CallingConvention, property.GetMethod.ReturnType, property.GetMethod.GetParameters().Select(p => p.ParameterType).ToArray());\n                        var ilGen = methodBuilder.GetILGenerator();\n                        ilGen.Emit(OpCodes.Ldarg_0);\n                        ilGen.Emit(OpCodes.Ldfld, field);\n                        ilGen.Emit(OpCodes.Ret);\n                        typeBuilder.DefineMethodOverride(methodBuilder, property.GetMethod);\n                        propertyBuilder.SetGetMethod(methodBuilder);\n                        propertyMethods.Add(property.GetMethod.Name);\n                    }\n                    if (property.CanWrite)\n                    {\n                        var methodBuilder = typeBuilder.DefineMethod(property.SetMethod!.Name, InterfaceMethodAttributes, property.SetMethod.CallingConvention, property.SetMethod.ReturnType, property.SetMethod.GetParameters().Select(p => p.ParameterType).ToArray());\n                        var ilGen = methodBuilder.GetILGenerator();\n                        ilGen.Emit(OpCodes.Ldarg_0);\n                        ilGen.Emit(OpCodes.Ldarg_1);\n                        ilGen.Emit(OpCodes.Stfld, field);\n                        ilGen.Emit(OpCodes.Ret);\n                        typeBuilder.DefineMethodOverride(methodBuilder, property.SetMethod);\n                        propertyBuilder.SetSetMethod(methodBuilder);\n                        propertyMethods.Add(property.SetMethod.Name);\n                    }\n                    foreach (var customAttributeData in property.CustomAttributes)\n                    {\n                        propertyBuilder.SetCustomAttribute(DefineCustomAttribute(customAttributeData));\n                    }\n                }\n\n                methods = implementedInterface.GetMethods(BindingFlags.Instance | BindingFlags.Public);\n                foreach (var method in methods.Where(x => !propertyMethods.Contains(x.Name) && !_ignoredMethods.Contains(x.Name)))\n                {\n                    MethodUtils.DefineInterfaceMethod(typeBuilder, method, null);\n                }\n            }\n\n            proxyType = Guard.NotNull(typeBuilder.CreateType());\n            _proxyTypes[proxyTypeName] = proxyType;\n            return proxyType;\n        }\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static Type CreateInterfaceProxy(Type interfaceType, Type? implementType)\n    {\n        Guard.NotNull(interfaceType);\n        if (!interfaceType.IsInterface)\n        {\n            throw new InvalidOperationException($\"{interfaceType.FullName} is not an interface\");\n        }\n\n        if (implementType is null)\n            return CreateInterfaceProxy(interfaceType);\n\n        var proxyTypeName = _proxyTypeNameResolver(interfaceType, implementType);\n\n        lock (_typeLock)\n        {\n            if (_proxyTypes.TryGetValue(proxyTypeName, out var proxyType))\n            {\n                return proxyType;\n            }\n            var typeBuilder = _moduleBuilder.DefineType(proxyTypeName, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed, null, [interfaceType]);\n            GenericParameterUtils.DefineGenericParameter(interfaceType, typeBuilder);\n\n            var targetField = typeBuilder.DefineField(TargetFieldName, implementType, FieldAttributes.Private);\n            // constructors\n            typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);\n\n            // properties\n            var propertyMethods = new HashSet<string>();\n            var properties = interfaceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);\n            foreach (var property in properties)\n            {\n                var propertyBuilder = typeBuilder.DefineProperty(property.Name, property.Attributes, property.PropertyType, Type.EmptyTypes);\n                if (property.CanRead)\n                {\n                    var methodBuilder = MethodUtils.DefineInterfaceMethod(typeBuilder, property.GetMethod!, targetField);\n                    typeBuilder.DefineMethodOverride(methodBuilder, property.GetMethod!);\n                    propertyBuilder.SetGetMethod(methodBuilder);\n                    propertyMethods.Add(property.GetMethod!.Name);\n                }\n                if (property.CanWrite)\n                {\n                    var methodBuilder = MethodUtils.DefineInterfaceMethod(typeBuilder, property.SetMethod!, targetField);\n                    typeBuilder.DefineMethodOverride(methodBuilder, property.SetMethod!);\n                    propertyBuilder.SetSetMethod(methodBuilder);\n                    propertyMethods.Add(property.SetMethod!.Name);\n                }\n                foreach (var customAttributeData in property.CustomAttributes)\n                {\n                    propertyBuilder.SetCustomAttribute(DefineCustomAttribute(customAttributeData));\n                }\n            }\n\n            //\n            var methods = interfaceType.GetMethods(BindingFlags.Instance | BindingFlags.Public);\n            foreach (var method in methods)\n            {\n                if (propertyMethods.Contains(method.Name) || _ignoredMethods.Contains(method.Name))\n                {\n                    continue;\n                }\n                MethodUtils.DefineInterfaceMethod(typeBuilder, method, targetField);\n            }\n\n            foreach (var implementedInterface in interfaceType.GetImplementedInterfaces())\n            {\n                properties = implementedInterface.GetProperties(BindingFlags.Instance | BindingFlags.Public);\n                foreach (var property in properties)\n                {\n                    var propertyBuilder = typeBuilder.DefineProperty(property.Name, property.Attributes, property.PropertyType, Type.EmptyTypes);\n                    var field = typeBuilder.DefineField($\"_{property.Name}\", property.PropertyType, FieldAttributes.Private);\n                    if (property.CanRead)\n                    {\n                        var methodBuilder = typeBuilder.DefineMethod(property.GetMethod!.Name, InterfaceMethodAttributes, property.GetMethod.CallingConvention, property.GetMethod.ReturnType, property.GetMethod.GetParameters().Select(p => p.ParameterType).ToArray());\n                        var ilGen = methodBuilder.GetILGenerator();\n                        ilGen.Emit(OpCodes.Ldarg_0);\n                        ilGen.Emit(OpCodes.Ldfld, field);\n                        ilGen.Emit(OpCodes.Ret);\n                        typeBuilder.DefineMethodOverride(methodBuilder, property.GetMethod);\n                        propertyBuilder.SetGetMethod(methodBuilder);\n                        propertyMethods.Add(property.GetMethod.Name);\n                    }\n                    if (property.CanWrite)\n                    {\n                        var methodBuilder = typeBuilder.DefineMethod(property.SetMethod!.Name, InterfaceMethodAttributes, property.SetMethod.CallingConvention, property.SetMethod.ReturnType, property.SetMethod.GetParameters().Select(p => p.ParameterType).ToArray());\n                        var ilGen = methodBuilder.GetILGenerator();\n                        ilGen.Emit(OpCodes.Ldarg_0);\n                        ilGen.Emit(OpCodes.Ldarg_1);\n                        ilGen.Emit(OpCodes.Stfld, field);\n                        ilGen.Emit(OpCodes.Ret);\n                        typeBuilder.DefineMethodOverride(methodBuilder, property.SetMethod);\n                        propertyBuilder.SetSetMethod(methodBuilder);\n                        propertyMethods.Add(property.SetMethod.Name);\n                    }\n                    foreach (var customAttributeData in property.CustomAttributes)\n                    {\n                        propertyBuilder.SetCustomAttribute(DefineCustomAttribute(customAttributeData));\n                    }\n                }\n\n                methods = implementedInterface.GetMethods(BindingFlags.Instance | BindingFlags.Public);\n                foreach (var method in methods.Where(x => !propertyMethods.Contains(x.Name) && !_ignoredMethods.Contains(x.Name)))\n                {\n                    MethodUtils.DefineInterfaceMethod(typeBuilder, method, targetField);\n                }\n            }\n\n            proxyType = Guard.NotNull(typeBuilder.CreateType());\n            _proxyTypes[proxyTypeName] = proxyType;\n            return proxyType;\n        }\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static Type CreateClassProxy(Type serviceType, Type? implementType)\n    {\n        Guard.NotNull(serviceType);\n\n        implementType ??= serviceType;\n        if (serviceType.IsSealed || implementType.IsSealed)\n        {\n            throw new InvalidOperationException(\"the class type is sealed\");\n        }\n        //\n        var proxyTypeName = _proxyTypeNameResolver(serviceType, implementType);\n        lock (_typeLock)\n        {\n            if (_proxyTypes.TryGetValue(proxyTypeName, out var proxyType))\n            {\n                return proxyType;\n            }\n\n            var typeBuilder = _moduleBuilder.DefineType(proxyTypeName, TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, implementType, Type.EmptyTypes);\n            GenericParameterUtils.DefineGenericParameter(implementType, typeBuilder);\n\n            var targetField = typeBuilder.DefineField(TargetFieldName, implementType, FieldAttributes.Private);\n\n            // constructors\n            var constructors = implementType.GetConstructors();\n            if (constructors.Length > 0)\n            {\n                foreach (var constructor in constructors)\n                {\n                    var constructorTypes = constructor.GetParameters().Select(o => o.ParameterType).ToArray();\n                    var constructorBuilder = typeBuilder.DefineConstructor(\n                        constructor.Attributes,\n                        constructor.CallingConvention,\n                        constructorTypes);\n                    foreach (var customAttribute in constructor.CustomAttributes)\n                    {\n                        constructorBuilder.SetCustomAttribute(DefineCustomAttribute(customAttribute));\n                    }\n\n                    var il = constructorBuilder.GetILGenerator();\n\n                    il.EmitThis();\n                    for (var i = 0; i < constructorTypes.Length; i++)\n                    {\n                        il.Emit(OpCodes.Ldarg, i + 1);\n                    }\n\n                    il.Call(constructor);\n                    il.Emit(OpCodes.Nop);\n\n                    il.EmitThis();\n                    il.EmitThis();\n                    il.Emit(OpCodes.Stfld, targetField);\n\n                    il.Emit(OpCodes.Ret);\n                }\n            }\n            else\n            {\n                var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Type.EmptyTypes);\n                var il = constructorBuilder.GetILGenerator();\n\n                il.EmitThis();\n                il.EmitThis();\n                il.Emit(OpCodes.Stfld, targetField);\n\n                il.Emit(OpCodes.Ret);\n            }\n            // properties\n            var propertyMethods = new HashSet<string>();\n            foreach (var property in serviceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))\n            {\n                if (property.IsVisibleAndVirtual())\n                {\n                    var propertyBuilder = typeBuilder.DefineProperty(property.Name, property.Attributes, property.PropertyType, Type.EmptyTypes);\n\n                    //inherit targetMethod's attribute\n                    foreach (var customAttributeData in property.CustomAttributes)\n                    {\n                        propertyBuilder.SetCustomAttribute(DefineCustomAttribute(customAttributeData));\n                    }\n\n                    if (property.CanRead)\n                    {\n                        propertyMethods.Add(property.GetMethod!.Name);\n\n                        var method = MethodUtils.DefineClassMethod(typeBuilder, property.GetMethod, targetField);\n                        propertyBuilder.SetGetMethod(method);\n                    }\n                    if (property.CanWrite)\n                    {\n                        propertyMethods.Add(property.SetMethod!.Name);\n\n                        var method = MethodUtils.DefineClassMethod(typeBuilder, property.SetMethod, targetField);\n                        propertyBuilder.SetSetMethod(method);\n                    }\n                }\n            }\n\n            // methods\n            var methods = serviceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)\n                    .Where(m => m.IsVirtual && !m.IsFinal && m.IsVisible() && !propertyMethods.Contains(m.Name) && !_ignoredMethods.Contains(m.Name))\n                    .ToArray();\n            foreach (var method in methods)\n            {\n                MethodUtils.DefineClassMethod(typeBuilder, method, targetField);\n            }\n\n            proxyType = Guard.NotNull(typeBuilder.CreateType());\n            _proxyTypes[proxyTypeName] = proxyType;\n            return proxyType;\n        }\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static void SetProxyTarget(object? proxyService, object? target)\n    {\n        if (null != proxyService && null != target)\n        {\n            var proxyServiceType = proxyService.GetType();\n            var targetField = proxyServiceType.GetField(TargetFieldName, BindingFlags.Instance | BindingFlags.NonPublic);\n            if (targetField != null && targetField.FieldType.IsInstanceOfType(target))\n            {\n                targetField.SetValue(proxyService, target);\n            }\n        }\n    }\n\n    private static class MethodUtils\n    {\n        [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n        [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n        public static MethodBuilder DefineInterfaceMethod(TypeBuilder typeBuilder, MethodInfo method, FieldBuilder? targetField)\n        {\n            var methodParameters = method.GetParameters();\n            var methodParameterTypes = methodParameters\n                    .Select(p => p.ParameterType)\n                    .ToArray();\n\n            var methodBuilder = typeBuilder.DefineMethod(method.Name\n                , InterfaceMethodAttributes,\n                method.CallingConvention,\n                method.ReturnType,\n                methodParameterTypes\n                );\n\n            GenericParameterUtils.DefineGenericParameter(method, methodBuilder);\n\n            foreach (var customAttribute in method.CustomAttributes)\n            {\n                methodBuilder.SetCustomAttribute(DefineCustomAttribute(customAttribute));\n            }\n            typeBuilder.DefineMethodOverride(methodBuilder, method);\n\n            var il = methodBuilder.GetILGenerator();\n\n            var localReturnValue = il.DeclareReturnValue(method.ReturnType)!;\n            var localCurrentMethod = il.DeclareLocal(typeof(MethodInfo));\n            var localMethodBase = il.DeclareLocal(typeof(MethodInfo));\n            var localParameters = il.DeclareLocal(typeof(object[]));\n\n            // var currentMethod = MethodBase.GetCurrentMethod();\n            il.Call(MethodInvokeHelper.GetCurrentMethod);\n            il.EmitConvertToType(typeof(MethodBase), typeof(MethodInfo));\n            il.Emit(OpCodes.Stloc, localCurrentMethod);\n\n            var targetMethod = targetField?.FieldType\n                .GetMethodBySignature(method)\n                ;\n\n            if (null != targetMethod)\n            {\n                il.EmitMethod(methodBuilder.IsGenericMethod\n                    ? targetMethod.MakeGenericMethod(methodBuilder.GetGenericArguments())\n                    : targetMethod);\n            }\n            else\n            {\n                il.EmitNull();\n            }\n\n            il.Emit(OpCodes.Stloc, localMethodBase);\n\n            // var parameters = new[] {a, b, c};\n            il.Emit(OpCodes.Ldc_I4, methodParameterTypes.Length);\n            il.Emit(OpCodes.Newarr, typeof(object));\n            if (methodParameterTypes.Length > 0)\n            {\n                for (var i = 0; i < methodParameterTypes.Length; i++)\n                {\n                    il.Emit(OpCodes.Dup);\n                    il.Emit(OpCodes.Ldc_I4, i);\n                    il.Emit(OpCodes.Ldarg, i + 1);\n                    if (methodParameterTypes[i].IsValueType)\n                    {\n                        il.Emit(OpCodes.Box, methodParameterTypes[i].UnderlyingSystemType);\n                    }\n\n                    il.Emit(OpCodes.Stelem_Ref);\n                }\n            }\n            il.Emit(OpCodes.Stloc, localParameters);\n\n            var localAspectInvocation = il.DeclareLocal(typeof(AspectInvocation));\n            il.Emit(OpCodes.Ldloc, localCurrentMethod);\n            il.Emit(OpCodes.Ldloc, localMethodBase);\n            il.EmitThis();\n\n            il.EmitThis();\n            if (null != targetField)\n            {\n                il.Emit(OpCodes.Ldfld, targetField);\n            }\n\n            il.Emit(OpCodes.Ldloc, localParameters);\n\n            il.New(MethodInvokeHelper.AspectInvocationConstructor);\n            il.Emit(OpCodes.Stloc, localAspectInvocation);\n\n            // AspectDelegate.Invoke(invocation);\n            il.Emit(OpCodes.Ldloc, localAspectInvocation);\n            il.Call(MethodInvokeHelper.InvokeAspectDelegateMethod);\n            il.Emit(OpCodes.Nop);\n\n            if (method.ReturnType != typeof(void))\n            {\n                // load return value\n                il.Emit(OpCodes.Ldloc, localAspectInvocation);\n                il.EmitCall(OpCodes.Callvirt, MethodInvokeHelper.GetInvocationReturnValueMethod, Type.EmptyTypes);\n\n                if (method.ReturnType != typeof(object))\n                {\n                    il.EmitCastToType(typeof(object), method.ReturnType);\n                }\n\n                il.Emit(OpCodes.Stloc, localReturnValue);\n                il.Emit(OpCodes.Ldloc, localReturnValue);\n            }\n\n            il.Emit(OpCodes.Ret);\n            return methodBuilder;\n        }\n\n        [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n        [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n        public static MethodBuilder DefineClassMethod(TypeBuilder typeBuilder, MethodInfo method, FieldBuilder? targetField)\n        {\n            var methodParameterTypes = method.GetParameters()\n                .Select(p => p.ParameterType)\n                .ToArray();\n\n            var methodAttributes = OverrideMethodAttributes;\n            if (method.Attributes.HasFlag(MethodAttributes.Public))\n            {\n                methodAttributes |= MethodAttributes.Public;\n            }\n            if (method.Attributes.HasFlag(MethodAttributes.Family))\n            {\n                methodAttributes |= MethodAttributes.Family;\n            }\n            if (method.Attributes.HasFlag(MethodAttributes.FamORAssem))\n            {\n                methodAttributes |= MethodAttributes.FamORAssem;\n            }\n            var methodBuilder = typeBuilder.DefineMethod(method.Name,\n                methodAttributes,\n                method.CallingConvention,\n                method.ReturnType,\n                methodParameterTypes\n            );\n\n            GenericParameterUtils.DefineGenericParameter(method, methodBuilder);\n\n            foreach (var customAttributeData in method.CustomAttributes)\n            {\n                methodBuilder.SetCustomAttribute(DefineCustomAttribute(customAttributeData));\n            }\n\n            var il = methodBuilder.GetILGenerator();\n\n            var localReturnValue = il.DeclareReturnValue(method.ReturnType)!;\n            var localCurrentMethod = il.DeclareLocal(typeof(MethodInfo));\n            var localMethodBase = il.DeclareLocal(typeof(MethodInfo));\n            var localParameters = il.DeclareLocal(typeof(object[]));\n\n            // var currentMethod = MethodBase.GetCurrentMethod();\n            il.Call(MethodInvokeHelper.GetCurrentMethod);\n            il.EmitConvertToType(typeof(MethodBase), typeof(MethodInfo));\n            il.Emit(OpCodes.Stloc, localCurrentMethod);\n\n            var targetMethod = targetField?.FieldType.GetMethodBySignature(method);\n            if (null != targetMethod)\n            {\n                il.EmitMethod(methodBuilder.IsGenericMethod\n                    ? targetMethod.MakeGenericMethod(methodBuilder.GetGenericArguments())\n                    : targetMethod);\n            }\n            else\n            {\n                il.EmitNull();\n            }\n            il.Emit(OpCodes.Stloc, localMethodBase);\n\n            // var parameters = new[] {a, b, c};\n            il.Emit(OpCodes.Ldc_I4, methodParameterTypes.Length);\n            il.Emit(OpCodes.Newarr, typeof(object));\n            if (methodParameterTypes.Length > 0)\n            {\n                for (var i = 0; i < methodParameterTypes.Length; i++)\n                {\n                    il.Emit(OpCodes.Dup);\n                    il.Emit(OpCodes.Ldc_I4, i);\n                    il.Emit(OpCodes.Ldarg, i + 1);\n                    if (methodParameterTypes[i].IsValueType)\n                    {\n                        il.Emit(OpCodes.Box, methodParameterTypes[i].UnderlyingSystemType);\n                    }\n\n                    il.Emit(OpCodes.Stelem_Ref);\n                }\n            }\n            il.Emit(OpCodes.Stloc, localParameters);\n\n            // var invocation = new MethodInvocation(method, methodBase, this, this, parameters);\n            var localAspectInvocation = il.DeclareLocal(typeof(AspectInvocation));\n            il.Emit(OpCodes.Ldloc, localCurrentMethod);\n            il.Emit(OpCodes.Ldloc, localMethodBase);\n            il.EmitThis();\n\n            il.EmitThis();\n            if (null != targetField)\n            {\n                il.Emit(OpCodes.Ldfld, targetField);\n            }\n\n            il.Emit(OpCodes.Ldloc, localParameters);\n\n            il.New(MethodInvokeHelper.AspectInvocationConstructor);\n            il.Emit(OpCodes.Stloc, localAspectInvocation);\n\n            // AspectDelegate.Invoke(invocation);\n            il.Emit(OpCodes.Ldloc, localAspectInvocation);\n            il.Call(MethodInvokeHelper.InvokeAspectDelegateMethod);\n\n            if (method.ReturnType != typeof(void))\n            {\n                // load return value\n                il.Emit(OpCodes.Ldloc, localAspectInvocation);\n\n                il.EmitCall(OpCodes.Callvirt, MethodInvokeHelper.GetInvocationReturnValueMethod, Type.EmptyTypes);\n\n                if (method.ReturnType.IsValueType)\n                {\n                    il.EmitCastToType(typeof(object), method.ReturnType);\n                }\n\n                il.Emit(OpCodes.Stloc, localReturnValue);\n                il.Emit(OpCodes.Ldloc, localReturnValue);\n            }\n\n            il.Emit(OpCodes.Ret);\n\n            return methodBuilder;\n        }\n    }\n\n    private static class GenericParameterUtils\n    {\n        [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n        [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n        public static void DefineGenericParameter(Type targetType, TypeBuilder typeBuilder)\n        {\n            if (!targetType.IsGenericTypeDefinition)\n            {\n                return;\n            }\n            var genericArguments = targetType.GetGenericArguments();\n            var genericArgumentsBuilders = typeBuilder\n                .DefineGenericParameters(genericArguments.Select(a => a.Name).ToArray());\n            for (var index = 0; index < genericArguments.Length; index++)\n            {\n                genericArgumentsBuilders[index].SetGenericParameterAttributes(ToClassGenericParameterAttributes(genericArguments[index].GenericParameterAttributes));\n                foreach (var constraint in genericArguments[index].GetGenericParameterConstraints())\n                {\n                    if (constraint.IsClass)\n                    {\n                        genericArgumentsBuilders[index].SetBaseTypeConstraint(constraint);\n                    }\n                    if (constraint.IsInterface)\n                    {\n                        genericArgumentsBuilders[index].SetInterfaceConstraints(constraint);\n                    }\n                }\n            }\n        }\n\n        [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n        [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n        public static void DefineGenericParameter(MethodInfo targetMethod, MethodBuilder methodBuilder)\n        {\n            if (!targetMethod.IsGenericMethod)\n            {\n                return;\n            }\n            var genericArguments = targetMethod.GetGenericArguments();\n            var genericArgumentsBuilders = methodBuilder\n                .DefineGenericParameters(genericArguments.Select(a => a.Name).ToArray());\n            for (var index = 0; index < genericArguments.Length; index++)\n            {\n                genericArgumentsBuilders[index].SetGenericParameterAttributes(genericArguments[index].GenericParameterAttributes);\n                foreach (var constraint in genericArguments[index].GetGenericParameterConstraints())\n                {\n                    if (constraint.IsClass)\n                    {\n                        genericArgumentsBuilders[index].SetBaseTypeConstraint(constraint);\n                    }\n                    if (constraint.IsInterface)\n                    {\n                        genericArgumentsBuilders[index].SetInterfaceConstraints(constraint);\n                    }\n                }\n            }\n        }\n\n        private static GenericParameterAttributes ToClassGenericParameterAttributes(GenericParameterAttributes attributes)\n        {\n            if (attributes == GenericParameterAttributes.None)\n            {\n                return GenericParameterAttributes.None;\n            }\n            if (attributes.HasFlag(GenericParameterAttributes.SpecialConstraintMask))\n            {\n                return GenericParameterAttributes.SpecialConstraintMask;\n            }\n            if (attributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint))\n            {\n                return GenericParameterAttributes.NotNullableValueTypeConstraint;\n            }\n            if (attributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint) && attributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint))\n            {\n                return GenericParameterAttributes.ReferenceTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint;\n            }\n            if (attributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint))\n            {\n                return GenericParameterAttributes.ReferenceTypeConstraint;\n            }\n            if (attributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint))\n            {\n                return GenericParameterAttributes.DefaultConstructorConstraint;\n            }\n            return GenericParameterAttributes.None;\n        }\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    private static CustomAttributeBuilder DefineCustomAttribute(CustomAttributeData customAttributeData)\n    {\n        // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract\n        if (customAttributeData.NamedArguments is null)\n            return new CustomAttributeBuilder(customAttributeData.Constructor,\n                customAttributeData.ConstructorArguments.Select(c => c.Value).ToArray());\n\n        var attributeTypeInfo = customAttributeData.AttributeType.GetTypeInfo();\n        var constructorArgs = customAttributeData.ConstructorArguments\n            .Select(ReadAttributeValue)\n            .ToArray();\n        var namedProperties = customAttributeData.NamedArguments\n            .Where(n => !n.IsField)\n            .Select(n => Guard.NotNull(attributeTypeInfo.GetProperty(n.MemberName)))\n            .ToArray();\n        var propertyValues = customAttributeData.NamedArguments\n            .Where(n => !n.IsField)\n            .Select(n => ReadAttributeValue(n.TypedValue)!)\n            .ToArray();\n        var namedFields = customAttributeData.NamedArguments.Where(n => n.IsField)\n            .Select(n => Guard.NotNull(attributeTypeInfo.GetField(n.MemberName)))\n            .ToArray();\n        var fieldValues = customAttributeData.NamedArguments.Where(n => n.IsField)\n            .Select(n => ReadAttributeValue(n.TypedValue)!)\n            .ToArray();\n        return new CustomAttributeBuilder(customAttributeData.Constructor, constructorArgs\n            , namedProperties\n            , propertyValues, namedFields, fieldValues);\n\n    }\n\n    private static object? ReadAttributeValue(CustomAttributeTypedArgument argument)\n    {\n        var value = argument.Value;\n        if (argument.ArgumentType.GetTypeInfo().IsArray == false)\n        {\n            return value;\n        }\n        //special case for handling arrays in attributes\n        //the actual type of \"value\" is ReadOnlyCollection<CustomAttributeTypedArgument>.\n        var arguments = ((IEnumerable<CustomAttributeTypedArgument>)Guard.NotNull(value))\n            .Select(m => m.Value)\n            .ToArray();\n        return arguments;\n    }\n\n#if NETSTANDARD2_0\n\n    private static Type CreateType(this TypeBuilder typeBuilder)\n    {\n        return typeBuilder.CreateTypeInfo()!.AsType();\n    }\n\n#endif\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/ServiceCollectionExtensions.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.DependencyInjection.Extensions;\nusing System.Linq.Expressions;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic static class ServiceCollectionExtensions\n{\n    public static IFluentAspectsBuilder AddFluentAspects(this IServiceCollection serviceCollection, Action<FluentAspectOptions> optionsAction)\n    {\n        Guard.NotNull(serviceCollection);\n        Guard.NotNull(optionsAction);\n        FluentAspects.Configure(optionsAction);\n        return AddFluentAspects(serviceCollection);\n    }\n\n    public static IFluentAspectsBuilder AddFluentAspects(this IServiceCollection serviceCollection)\n    {\n        Guard.NotNull(serviceCollection);\n\n        serviceCollection.TryAddTransient<IProxyTypeFactory, DefaultProxyTypeFactory>();\n        serviceCollection.TryAddTransient<IProxyFactory, DefaultProxyFactory>();\n        serviceCollection.TryAddSingleton(FluentConfigInterceptorResolver.Instance);\n\n        return new FluentAspectsBuilder(serviceCollection);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection AddProxyService<TService, TImplement>(this IServiceCollection serviceCollection, ServiceLifetime serviceLifetime)\n        where TImplement : TService\n        where TService : class\n    {\n        serviceCollection.Add(new ServiceDescriptor(typeof(TService), sp =>\n        {\n            var proxyFactory = sp.GetRequiredService<IProxyFactory>();\n            return proxyFactory.CreateProxy<TService, TImplement>();\n        }, serviceLifetime));\n        return serviceCollection;\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection AddSingletonProxy<TService, TImplement>(this IServiceCollection serviceCollection)\n        where TImplement : TService\n        where TService : class\n    {\n        return serviceCollection.AddProxyService<TService, TImplement>(ServiceLifetime.Singleton);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection AddScopedProxy<TService, TImplement>(this IServiceCollection serviceCollection)\n        where TImplement : TService\n        where TService : class\n    {\n        return serviceCollection.AddProxyService<TService, TImplement>(ServiceLifetime.Scoped);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection AddTransientProxy<TService, TImplement>(this IServiceCollection serviceCollection)\n        where TImplement : TService\n        where TService : class\n    {\n        return serviceCollection.AddProxyService<TService, TImplement>(ServiceLifetime.Transient);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection AddProxyService<TService>(this IServiceCollection serviceCollection, ServiceLifetime serviceLifetime)\n        where TService : class\n    {\n        serviceCollection.Add(new ServiceDescriptor(typeof(TService), sp =>\n        {\n            var proxyFactory = sp.GetRequiredService<IProxyFactory>();\n            return proxyFactory.CreateProxy<TService>();\n        }, serviceLifetime));\n\n        return serviceCollection;\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection AddSingletonProxy<TService>(this IServiceCollection serviceCollection)\n        where TService : class =>\n        serviceCollection.AddProxyService<TService>(ServiceLifetime.Singleton);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection AddScopedProxy<TService>(this IServiceCollection serviceCollection)\n        where TService : class =>\n        serviceCollection.AddProxyService<TService>(ServiceLifetime.Scoped);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection AddTransientProxy<TService>(this IServiceCollection serviceCollection)\n        where TService : class =>\n        serviceCollection.AddProxyService<TService>(ServiceLifetime.Transient);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceProvider BuildFluentAspectsProvider(this IServiceCollection serviceCollection,\n        Action<FluentAspectOptions>? optionsAction,\n        Action<IFluentAspectsBuilder>? aspectBuildAction = null,\n        Expression<Func<Type, bool>>? ignoreTypesFilter = null,\n        ServiceProviderOptions? serviceProviderOptions = null)\n    {\n        IServiceCollection services = new ServiceCollection();\n\n        var aspectBuilder = null != optionsAction\n            ? serviceCollection.AddFluentAspects(optionsAction)\n            : serviceCollection.AddFluentAspects();\n        aspectBuildAction?.Invoke(aspectBuilder);\n\n        Expression<Func<Type, bool>> ignoreTypesExpression = t => \"WeihanLi.Common.Aspect\".Equals(t.Namespace);\n        if (null != ignoreTypesFilter)\n        {\n            ignoreTypesExpression = ignoreTypesExpression.Or(ignoreTypesFilter);\n        }\n\n        var ignoreTypesPredicate = ignoreTypesExpression.Compile();\n\n        using (var serviceProvider = serviceCollection.BuildServiceProvider())\n        {\n            var proxyTypeFactory = serviceProvider.GetRequiredService<IProxyTypeFactory>();\n\n            foreach (var descriptor in serviceCollection)\n            {\n                if (descriptor.ServiceType.IsSealed\n                    || descriptor.ServiceType.IsNotPublic\n                    || descriptor.ServiceType.IsGenericTypeDefinition\n                )\n                {\n                    services.Add(descriptor);\n                    continue;\n                }\n\n                if (ignoreTypesPredicate(descriptor.ServiceType))\n                {\n                    services.Add(descriptor);\n                    continue;\n                }\n\n                if (descriptor.ImplementationType != null)\n                {\n                    if (descriptor.ImplementationType.IsNotPublic\n                        || descriptor.ImplementationType.IsProxyType()\n                    )\n                    {\n                        services.Add(descriptor);\n                        continue;\n                    }\n\n                    if (descriptor.ServiceType.IsClass\n                        && descriptor.ImplementationType.IsSealed)\n                    {\n                        services.Add(descriptor);\n                        continue;\n                    }\n\n                    if (descriptor.ServiceType.IsGenericTypeDefinition\n                        || descriptor.ImplementationType.IsGenericTypeDefinition)\n                    {\n                        var proxyType = proxyTypeFactory.CreateProxyType(descriptor.ServiceType, descriptor.ImplementationType);\n                        services.Add(new ServiceDescriptor(descriptor.ServiceType, proxyType,\n                            descriptor.Lifetime));\n                        continue;\n                    }\n                }\n\n                Func<IServiceProvider, object>? serviceFactory = null;\n                if (descriptor.ImplementationInstance != null)\n                {\n                    if (descriptor.ImplementationInstance.GetType().IsPublic)\n                    {\n                        serviceFactory = provider => provider.GetRequiredService<IProxyFactory>()\n                            .CreateProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance);\n                    }\n                }\n                else if (descriptor.ImplementationType != null)\n                {\n                    serviceFactory = provider =>\n                    {\n                        var proxy = provider.GetRequiredService<IProxyFactory>()\n                            .CreateProxy(descriptor.ServiceType, descriptor.ImplementationType);\n                        return proxy;\n                    };\n                }\n                else if (descriptor.ImplementationFactory != null)\n                {\n                    serviceFactory = provider =>\n                    {\n                        var implement = descriptor.ImplementationFactory(provider);\n                        var implementType = implement.GetType();\n                        if (implementType.IsNotPublic\n                            || implementType.IsProxyType())\n                        {\n                            return implement;\n                        }\n\n                        return provider.ResolveRequiredService<IProxyFactory>()\n                            .CreateProxyWithTarget(descriptor.ServiceType, implement);\n                    };\n                }\n\n                if (null != serviceFactory)\n                {\n                    services.Add(new ServiceDescriptor(descriptor.ServiceType, serviceFactory,\n                        descriptor.Lifetime));\n                }\n                else\n                {\n                    services.Add(descriptor);\n                }\n            }\n        }\n\n        DependencyResolver.SetDependencyResolver(services);\n\n        return services.BuildServiceProvider(serviceProviderOptions ?? new ServiceProviderOptions());\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Aspect/ServiceContainerBuilderExtensions.cs",
    "content": "﻿using System.Linq.Expressions;\nusing WeihanLi.Common.DependencyInjection;\nusing WeihanLi.Extensions;\nusing ServiceLifetime = WeihanLi.Common.DependencyInjection.ServiceLifetime;\n\nnamespace WeihanLi.Common.Aspect;\n\npublic static class ServiceContainerBuilderExtensions\n{\n    public static IFluentAspectsServiceContainerBuilder AddFluentAspects(this IServiceContainerBuilder serviceCollection, Action<FluentAspectOptions> optionsAction)\n    {\n        Guard.NotNull(serviceCollection);\n        Guard.NotNull(optionsAction);\n        FluentAspects.Configure(optionsAction);\n        return AddFluentAspects(serviceCollection);\n    }\n\n    public static IFluentAspectsServiceContainerBuilder AddFluentAspects(this IServiceContainerBuilder serviceCollection)\n    {\n        Guard.NotNull(serviceCollection);\n\n        serviceCollection.AddTransient<IProxyTypeFactory, DefaultProxyTypeFactory>();\n        serviceCollection.AddTransient<IProxyFactory, DefaultProxyFactory>();\n        serviceCollection.AddSingleton(FluentConfigInterceptorResolver.Instance);\n\n        return new FluentAspectsServiceContainerBuilder(serviceCollection);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder AddProxyService<TService, TImplement>(this IServiceContainerBuilder serviceCollection, ServiceLifetime serviceLifetime)\n        where TImplement : TService\n        where TService : class\n    {\n        Guard.NotNull(serviceCollection);\n\n        serviceCollection.Add(new ServiceDefinition(typeof(TService), sp =>\n        {\n            var proxyFactory = sp.ResolveRequiredService<IProxyFactory>();\n            return proxyFactory.CreateProxy<TService, TImplement>();\n        }, serviceLifetime));\n        return serviceCollection;\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder AddSingletonProxy<TService, TImplement>(this IServiceContainerBuilder serviceCollection)\n        where TImplement : TService\n        where TService : class\n    {\n        Guard.NotNull(serviceCollection);\n        return serviceCollection.AddProxyService<TService, TImplement>(ServiceLifetime.Singleton);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder AddScopedProxy<TService, TImplement>(this IServiceContainerBuilder serviceCollection)\n        where TImplement : TService\n        where TService : class\n    {\n        Guard.NotNull(serviceCollection);\n        return serviceCollection.AddProxyService<TService, TImplement>(ServiceLifetime.Scoped);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder AddTransientProxy<TService, TImplement>(this IServiceContainerBuilder serviceCollection)\n        where TImplement : TService\n        where TService : class\n    {\n        Guard.NotNull(serviceCollection);\n        return serviceCollection.AddProxyService<TService, TImplement>(ServiceLifetime.Transient);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder AddProxyService<TService>(this IServiceContainerBuilder serviceCollection, ServiceLifetime serviceLifetime)\n        where TService : class\n    {\n        Guard.NotNull(serviceCollection);\n        serviceCollection.Add(new ServiceDefinition(typeof(TService), sp =>\n        {\n            var proxyFactory = sp.ResolveRequiredService<IProxyFactory>();\n            return proxyFactory.CreateProxy<TService>();\n        }, serviceLifetime));\n\n        return serviceCollection;\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder AddSingletonProxy<TService>(this IServiceContainerBuilder serviceCollection)\n        where TService : class =>\n        serviceCollection.AddProxyService<TService>(ServiceLifetime.Singleton);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder AddScopedProxy<TService>(this IServiceContainerBuilder serviceCollection)\n        where TService : class =>\n        serviceCollection.AddProxyService<TService>(ServiceLifetime.Scoped);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder AddTransientProxy<TService>(this IServiceContainerBuilder serviceCollection)\n        where TService : class =>\n        serviceCollection.AddProxyService<TService>(ServiceLifetime.Transient);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainer BuildFluentAspectsContainer(this IServiceContainerBuilder serviceCollection,\n        Action<FluentAspectOptions>? optionsAction,\n        Action<IFluentAspectsServiceContainerBuilder>? aspectBuildAction = null,\n        Expression<Func<Type, bool>>? ignoreTypesFilter = null)\n    {\n        Guard.NotNull(serviceCollection);\n        var services = new ServiceContainerBuilder();\n\n        var aspectBuilder = null != optionsAction\n            ? serviceCollection.AddFluentAspects(optionsAction)\n            : serviceCollection.AddFluentAspects();\n        aspectBuildAction?.Invoke(aspectBuilder);\n\n        Expression<Func<Type, bool>> ignoreTypesExpression = t => \"WeihanLi.Common.Aspect\".Equals(t.Namespace);\n        if (null != ignoreTypesFilter)\n        {\n            ignoreTypesExpression = ignoreTypesExpression.Or(ignoreTypesFilter);\n        }\n\n        var ignoreTypesPredicate = ignoreTypesExpression.Compile();\n\n        using (var serviceProvider = serviceCollection.Build())\n        {\n            var proxyTypeFactory = serviceProvider.ResolveRequiredService<IProxyTypeFactory>();\n\n            foreach (var descriptor in serviceCollection)\n            {\n                if (descriptor.ServiceType.IsSealed\n                    || descriptor.ServiceType.IsNotPublic\n                    || descriptor.ServiceType.IsGenericTypeDefinition\n                )\n                {\n                    services.Add(descriptor);\n                    continue;\n                }\n\n                if (ignoreTypesPredicate(descriptor.ServiceType))\n                {\n                    services.Add(descriptor);\n                    continue;\n                }\n\n                if (descriptor.ImplementType != null)\n                {\n                    if (descriptor.ImplementType.IsNotPublic\n                        || descriptor.ImplementType.IsProxyType()\n                    )\n                    {\n                        services.Add(descriptor);\n                        continue;\n                    }\n\n                    if (descriptor.ServiceType.IsClass\n                        && descriptor.ImplementType.IsSealed)\n                    {\n                        services.Add(descriptor);\n                        continue;\n                    }\n\n                    if (descriptor.ServiceType.IsGenericTypeDefinition\n                        || descriptor.ImplementType.IsGenericTypeDefinition)\n                    {\n                        var proxyType = proxyTypeFactory.CreateProxyType(descriptor.ServiceType, descriptor.ImplementType);\n                        services.Add(new ServiceDefinition(descriptor.ServiceType, proxyType,\n                            descriptor.ServiceLifetime));\n                        continue;\n                    }\n                }\n\n                Func<IServiceProvider, object?>? serviceFactory = null;\n\n                if (descriptor.ImplementationInstance != null)\n                {\n                    if (descriptor.ImplementationInstance.GetType().IsPublic)\n                    {\n                        serviceFactory = provider => provider.ResolveRequiredService<IProxyFactory>()\n                            .CreateProxyWithTarget(descriptor.ServiceType, descriptor.ImplementationInstance);\n                    }\n                }\n                else if (descriptor.ImplementType != null)\n                {\n                    serviceFactory = provider =>\n                    {\n                        var proxy = provider.ResolveRequiredService<IProxyFactory>()\n                            .CreateProxy(descriptor.ServiceType, descriptor.ImplementType);\n                        return proxy;\n                    };\n                }\n                else if (descriptor.ImplementationFactory != null)\n                {\n                    serviceFactory = provider =>\n                    {\n                        var implement = descriptor.ImplementationFactory(provider);\n                        if (implement == null)\n                        {\n                            return null;\n                        }\n\n                        var implementType = implement.GetType();\n                        if (implementType.IsNotPublic\n                            || implementType.IsProxyType())\n                        {\n                            return implement;\n                        }\n\n                        return provider.ResolveRequiredService<IProxyFactory>()\n                            .CreateProxyWithTarget(descriptor.ServiceType, implement);\n                    };\n                }\n\n                if (serviceFactory != null)\n                {\n                    services.Add(new ServiceDefinition(descriptor.ServiceType, serviceFactory!,\n                        descriptor.ServiceLifetime));\n                }\n                else\n                {\n                    services.Add(descriptor);\n                }\n            }\n        }\n\n        var container = services.Build();\n        DependencyResolver.SetDependencyResolver(container);\n        return container;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/CacheUtil.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing WeihanLi.Common.DependencyInjection;\n\nnamespace WeihanLi.Common;\n\npublic static class CacheUtil\n{\n    private static readonly ConcurrentDictionary<Type, PropertyInfo[]> TypePropertyCache = new();\n    private static readonly ConcurrentDictionary<Type, FieldInfo[]> TypeFieldCache = new();\n\n    public static PropertyInfo[] GetTypeProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type)\n    {\n        Guard.NotNull(type);\n        return TypePropertyCache.GetOrAdd(type, _ => type.GetProperties());\n    }\n\n    public static FieldInfo[] GetTypeFields([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] Type type)\n    {\n        Guard.NotNull(type);\n        return TypeFieldCache.GetOrAdd(type, _ => type.GetFields());\n    }\n\n    internal static readonly ConcurrentDictionary<Type, MethodInfo[]> TypeMethodCache = new();\n\n    internal static readonly ConcurrentDictionary<Type, Func<ServiceContainer, object>> TypeNewFuncCache = new();\n\n    internal static readonly ConcurrentDictionary<Type, ConstructorInfo?> TypeConstructorCache = new();\n\n    internal static readonly ConcurrentDictionary<Type, Func<object>> TypeEmptyConstructorFuncCache = new();\n\n    internal static readonly ConcurrentDictionary<Type, Func<object?[], object>> TypeConstructorFuncCache = new();\n\n    internal static readonly ConcurrentDictionary<PropertyInfo, Func<object, object?>?> PropertyValueGetters = new();\n\n    internal static readonly ConcurrentDictionary<PropertyInfo, Action<object, object?>?> PropertyValueSetters = new();\n}\n\ninternal static class StrongTypedCache<T>\n{\n    public static readonly ConcurrentDictionary<PropertyInfo, Func<T, object?>?> PropertyValueGetters = new();\n\n    public static readonly ConcurrentDictionary<PropertyInfo, Action<T, object?>?> PropertyValueSetters = new();\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Compressor/DataCompressor.cs",
    "content": "﻿using WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Compressor;\n\n/// <summary>\n/// DataCompressor\n/// </summary>\npublic interface IDataCompressor\n{\n    /// <summary>\n    /// compress data\n    /// </summary>\n    /// <param name=\"sourceData\">source data</param>\n    /// <returns>compressed data</returns>\n    byte[] Compress(byte[] sourceData);\n\n    /// <summary>\n    /// compress data async\n    /// </summary>\n    /// <param name=\"sourceData\">source data</param>\n    /// <returns>compressed data</returns>\n    Task<byte[]> CompressAsync(byte[] sourceData);\n\n    /// <summary>\n    /// decompress compressed data\n    /// </summary>\n    /// <param name=\"compressedData\">compressed data</param>\n    /// <returns>source data</returns>\n    byte[] Decompress(byte[] compressedData);\n\n    /// <summary>\n    /// decompress compressed data async\n    /// </summary>\n    /// <param name=\"compressedData\">compressed data</param>\n    /// <returns>source data</returns>\n    Task<byte[]> DecompressAsync(byte[] compressedData);\n}\n\n/// <summary>\n/// NullDataCompressor\n/// do nothing compress\n/// </summary>\npublic sealed class NullDataCompressor : IDataCompressor\n{\n    public byte[] Compress(byte[] sourceData)\n    {\n        return sourceData;\n    }\n\n    public Task<byte[]> CompressAsync(byte[] sourceData)\n    {\n        return Task.FromResult(sourceData);\n    }\n\n    public byte[] Decompress(byte[] compressedData)\n    {\n        return compressedData;\n    }\n\n    public Task<byte[]> DecompressAsync(byte[] compressedData)\n    {\n        return Task.FromResult(compressedData);\n    }\n}\n\n/// <summary>\n/// GZipDataCompressor\n/// </summary>\npublic class GZipDataCompressor : IDataCompressor\n{\n    public byte[] Compress(byte[] sourceData)\n    {\n        return sourceData.CompressGZip();\n    }\n\n    public Task<byte[]> CompressAsync(byte[] sourceData)\n    {\n        return sourceData.CompressGZipAsync();\n    }\n\n    public byte[] Decompress(byte[] compressedData)\n    {\n        return compressedData.DecompressGZip();\n    }\n\n    public Task<byte[]> DecompressAsync(byte[] compressedData)\n    {\n        return compressedData.DecompressGZipAsync();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Data/Expressions/DateTimeExpressionParser.cs",
    "content": "﻿using System.Linq.Expressions;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Common.Data;\n\n/// <summary>\n/// DateTime Expression Parser\n/// </summary>\ninternal static partial class SqlExpressionParser\n{\n    public static string ParseDateTimeMemberAccess(MemberExpression exp, IDictionary<string, string>? columnMappings)\n    {\n        return exp.Member.Name switch\n        {\n            \"Now\" => $\"GETDATE()\",\n            \"UtcNow\" => $\"GETUTCDATE()\",\n            \"Today\" => \"CONVERT(CHAR(10), GETDATE(),120)\",\n            _ => string.Empty,\n        };\n    }\n\n    public static string ParseDateTimeMethodCall(MethodCallExpression exp, IDictionary<string, string>? columnMappings)\n    {\n        return string.Empty;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Data/Expressions/StringExpressionParser.cs",
    "content": "﻿using System.Linq.Expressions;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Common.Data;\n\n/// <summary>\n/// String Expression Parser\n/// </summary>\ninternal static partial class SqlExpressionParser\n{\n    public static string ParseStringMemberAccess(MemberExpression exp, IDictionary<string, string>? columnMappings)\n    {\n        return exp.Member.Name switch\n        {\n            \"Empty\" => string.Empty,\n            \"Length\" => $\"LEN({ParseExpression(exp.Expression, columnMappings)})\",\n            _ => throw new NotImplementedException(),\n        };\n    }\n\n    public static string ParseStringMethodCall(MethodCallExpression exp, IDictionary<string, string>? columnMappings)\n    {\n        if (exp.Object == null)\n        {\n            switch (exp.Method.Name)\n            {\n                case \"IsNullOrEmpty\":\n                    return \"1=1\";\n\n                case \"IsNullOrWhitespace\":\n                    return \"1=1\";\n\n                case \"IsNotNullOrEmpty\":\n                    return \"1=0\";\n\n                case \"IsNotNullOrWhitespace\":\n                    return \"1=0\";\n            }\n        }\n        else\n        {\n            var left = ParseExpression(exp.Object);\n            var arg0 = string.Empty;\n            if (exp.Arguments.Count > 0)\n            {\n                arg0 = ParseExpression(exp.Arguments[0]);\n            }\n\n            switch (exp.Method.Name)\n            {\n                case \"IsNullOrEmpty\":\n                    return $\"ISNULL({left}, '') = ''\";\n\n                case \"IsNullOrWhitespace\":\n                    return $\"TRIM(ISNULL({left}, '')) = ''\";\n\n                case \"Contains\":\n                    if (arg0 == \"NULL\")\n                    {\n                        return $\"{left} IS NULL\";\n                    }\n                    return $\"{left} LIKE N'%{(arg0.StartsWith(\"N'\") ? arg0.Replace(\"N'\", \"\").Replace(\"'\", \"\") : arg0.Replace(\"'\", \"\"))}%'\";\n\n                case \"StartsWith\":\n                    if (arg0 == \"NULL\")\n                    {\n                        return $\"{left} IS NULL\";\n                    }\n                    return $\"({left}) LIKE N'{(arg0.StartsWith(\"N'\") ? arg0.Replace(\"N'\", \"\").Replace(\"'\", \"\") : arg0.Replace(\"'\", \"\"))}%'\";\n\n                case \"EndWith\":\n                    if (arg0 == \"NULL\")\n                    {\n                        return $\"{left} IS NULL\";\n                    }\n                    return $\"({left}) LIKE N'%{(arg0.StartsWith(\"N'\") ? arg0.Replace(\"N'\", \"\").Replace(\"'\", \"\") : arg0.Replace(\"'\", \"\"))}'\";\n\n                case \"ToLower\": return $\"LOWER({left})\";\n                case \"ToUpper\": return $\"UPPER({left})\";\n\n                case \"Trim\": return $\"TRIM({left})\";\n                case \"TrimStart\": return $\"LTRIM({left})\";\n                case \"TrimEnd\": return $\"RTRIM({left})\";\n                case \"Equals\":\n                    if (arg0 == \"NULL\")\n                    {\n                        return $\"{left} IS NULL\";\n                    }\n                    return $\"({left} = {arg0})\";\n            }\n        }\n        throw new NotImplementedException($\"Method {exp.Method.Name} had not supported!\");\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Data/IRepository.cs",
    "content": "﻿using System.Linq.Expressions;\nusing WeihanLi.Common.Models;\n\nnamespace WeihanLi.Common.Data;\n\npublic interface IReadOnlyRepository<TEntity>\n{\n    /// <summary>\n    /// Count\n    /// </summary>\n    int Count(Expression<Func<TEntity, bool>> whereExpression);\n\n    Task<int> CountAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// LongCount\n    /// </summary>\n    long LongCount(Expression<Func<TEntity, bool>> whereExpression);\n\n    Task<long> LongCountAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Exist\n    /// </summary>\n    bool Exist(Expression<Func<TEntity, bool>> whereExpression);\n\n    Task<bool> ExistAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Get top 1 entity\n    /// </summary>\n    TEntity? Fetch(Expression<Func<TEntity, bool>> whereExpression);\n\n    Task<TEntity?> FetchAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default);\n\n    TEntity? Fetch<TProperty>(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false);\n\n    Task<TEntity?> FetchAsync<TProperty>(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Get List\n    /// </summary>\n    /// <param name=\"whereExpression\">whereExpression</param>\n    /// <returns></returns>\n    List<TEntity> Select(Expression<Func<TEntity, bool>> whereExpression);\n\n    /// <summary>\n    /// Get Entity List\n    /// </summary>\n    /// <param name=\"whereExpression\">where Expression</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns></returns>\n    Task<List<TEntity>> SelectAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Get List, Top n\n    /// </summary>\n    /// <param name=\"count\">count</param>\n    /// <param name=\"whereExpression\">whereExpression</param>\n    /// <param name=\"orderByExpression\">orderBy</param>\n    /// <param name=\"ascending\">is ascending</param>\n    /// <returns></returns>\n    List<TEntity> Select<TProperty>(int count, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false);\n\n    /// <summary>\n    /// Get List, Top n\n    /// </summary>\n    /// <param name=\"count\">count</param>\n    /// <param name=\"whereExpression\">whereExpression</param>\n    /// <param name=\"orderByExpression\">orderBy</param>\n    /// <param name=\"ascending\">is ascending</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns></returns>\n    Task<List<TEntity>> SelectAsync<TProperty>(int count, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Get Paged List\n    /// </summary>\n    /// <typeparam name=\"TProperty\">property to orderBy</typeparam>\n    /// <param name=\"pageNumber\">pageNumber from 1</param>\n    /// <param name=\"pageSize\">pageSize</param>\n    /// <param name=\"whereExpression\">whereExpression</param>\n    /// <param name=\"orderByExpression\">orderByExpression</param>\n    /// <param name=\"ascending\">is ascending</param>\n    /// <returns></returns>\n    IPagedListResult<TEntity> Paged<TProperty>(int pageNumber, int pageSize, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false);\n\n    /// <summary>\n    /// Get Paged List\n    /// </summary>\n    /// <typeparam name=\"TProperty\">property to orderBy</typeparam>\n    /// <param name=\"pageNumber\">pageNumber from 1</param>\n    /// <param name=\"pageSize\">pageSize</param>\n    /// <param name=\"whereExpression\">whereExpression</param>\n    /// <param name=\"orderByExpression\">orderByExpression</param>\n    /// <param name=\"ascending\">is ascending</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns></returns>\n    Task<IPagedListResult<TEntity>> PagedAsync<TProperty>(int pageNumber, int pageSize, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false, CancellationToken cancellationToken = default);\n}\n\n/// <summary>\n/// Represents an entity repository\n/// </summary>\n/// <typeparam name=\"TEntity\">Entity type</typeparam>\npublic interface IRepository<TEntity> : IReadOnlyRepository<TEntity>\n{\n    /// <summary>\n    /// Insert a entity\n    /// </summary>\n    /// <param name=\"entity\">Entity</param>\n    int Insert(TEntity entity);\n\n    /// <summary>\n    /// Insert a entity\n    /// </summary>\n    /// <param name=\"entity\">Entity</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    Task<int> InsertAsync(TEntity entity, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Insert entities\n    /// </summary>\n    /// <param name=\"entities\">Entities</param>\n    int Insert(IEnumerable<TEntity> entities);\n\n    /// <summary>\n    /// Insert entities\n    /// </summary>\n    /// <param name=\"entities\">Entities</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>rows affected</returns>\n    Task<int> InsertAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// update entity specific property by where\n    /// </summary>\n    /// <typeparam name=\"TProperty\">TProperty</typeparam>\n    /// <param name=\"whereExpression\">where</param>\n    /// <param name=\"propertyExpression\">property</param>\n    /// <param name=\"value\">new property value</param>\n    /// <returns></returns>\n    int Update<TProperty>(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> propertyExpression, object? value);\n\n    /// <summary>\n    /// update entity specific property by where\n    /// </summary>\n    /// <typeparam name=\"TProperty\">TProperty</typeparam>\n    /// <param name=\"whereExpression\">where</param>\n    /// <param name=\"propertyExpression\">property</param>\n    /// <param name=\"value\">new property value</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns></returns>\n    Task<int> UpdateAsync<TProperty>(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> propertyExpression, object? value, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Update entities properties by where\n    /// </summary>\n    /// <param name=\"whereExpression\">whereExpression</param>\n    /// <param name=\"propertyValues\">propertyValues to update</param>\n    /// <returns>updated rows count</returns>\n    int Update(Expression<Func<TEntity, bool>> whereExpression, IDictionary<string, object?> propertyValues);\n\n    /// <summary>\n    /// update entities with specific properties\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <param name=\"propertyExpressions\">propertyExpressions</param>\n    /// <returns>rows affected</returns>\n    int Update(TEntity entity, params Expression<Func<TEntity, object?>>[] propertyExpressions);\n\n    /// <summary>\n    /// Update entity without specific properties\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <param name=\"propertyExpressions\">properties not to update</param>\n    /// <returns>affected rows</returns>\n    int UpdateWithout(TEntity entity, params Expression<Func<TEntity, object?>>[] propertyExpressions);\n\n    /// <summary>\n    /// update entities with specific properties\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <param name=\"propertyNames\">propertyNames</param>\n    /// <returns>rows affected</returns>\n    int Update(TEntity entity, params string[] propertyNames);\n\n    /// <summary>\n    /// Update entity without specific properties\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <param name=\"propertyNames\">properties not to update</param>\n    /// <returns>affected rows</returns>\n    int UpdateWithout(TEntity entity, params string[] propertyNames);\n\n    /// <summary>\n    /// Update entity without specific properties\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <param name=\"propertyNames\">properties not to update</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>affected rows</returns>\n    Task<int> UpdateWithoutAsync(TEntity entity, string[] propertyNames, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// update entities with specific properties\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <param name=\"propertyExpressions\">propertyExpressions</param>\n    /// <param name=\"cancellationToken\"></param>\n    /// <returns>rows affected</returns>\n    Task<int> UpdateAsync(TEntity entity, Expression<Func<TEntity, object?>>[] propertyExpressions, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Update entity without specific properties\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <param name=\"propertyExpressions\">properties not to update</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>affected rows</returns>\n    Task<int> UpdateWithoutAsync(TEntity entity, Expression<Func<TEntity, object?>>[] propertyExpressions, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// update entities with specific properties\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <param name=\"propertyNames\">propertyNames</param>\n    /// <param name=\"cancellationToken\"></param>\n    /// <returns>rows affected</returns>\n    Task<int> UpdateAsync(TEntity entity, string[] propertyNames, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Update entities properties by where\n    /// </summary>\n    /// <param name=\"whereExpression\">whereExpression</param>\n    /// <param name=\"propertyValues\">propertyValues to update</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>updated rows count</returns>\n    Task<int> UpdateAsync(Expression<Func<TEntity, bool>> whereExpression, IDictionary<string, object?> propertyValues, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// Delete entities by where\n    /// </summary>\n    /// <param name=\"whereExpression\">whereExpression</param>\n    int Delete(Expression<Func<TEntity, bool>> whereExpression);\n\n    /// <summary>\n    /// delete entity\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <returns>rows affected</returns>\n    int Delete(TEntity entity);\n\n    /// <summary>\n    /// Delete entities by  where\n    /// </summary>\n    /// <param name=\"whereExpression\">whereExpression</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    Task<int> DeleteAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default);\n\n    /// <summary>\n    /// delete entity async\n    /// </summary>\n    /// <param name=\"entity\">entity</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>rows affected</returns>\n    Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Data/IUnitOfWork.cs",
    "content": "﻿namespace WeihanLi.Common.Data;\n\npublic interface IUnitOfWork\n{\n    void Commit();\n\n    Task CommitAsync(CancellationToken cancellationToken = default);\n\n    void Rollback();\n\n    Task RollbackAsync(CancellationToken cancellationToken = default);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Data/Repository.cs",
    "content": "﻿using System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Data.Common;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing System.Text;\nusing WeihanLi.Common.Models;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Data;\n\n[CLSCompliant(false)]\npublic class Repository<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties)] TEntity>(Func<DbConnection> dbConnectionFunc) : IRepository<TEntity> where TEntity : new()\n{\n    #region TODO: Cache External\n\n    private readonly Lazy<Dictionary<string, string>> PrimaryKeyColumns = new(() =>\n              CacheUtil.GetTypeProperties(typeof(TEntity))\n              .Any(x => x.IsDefined(typeof(KeyAttribute)))\n              ?\n              CacheUtil.GetTypeProperties(typeof(TEntity))\n              .ToDictionary(x => x.Name, x => x.GetColumnName())\n              :\n                  new Dictionary<string, string>(1)\n                  {\n                    { \"Id\",\n                        CacheUtil.GetTypeProperties(typeof(TEntity))\n                        .FirstOrDefault(x => x.Name.Equals(\"Id\"))?.GetColumnName()\n                        ?? throw new InvalidOperationException(\"no primary key found\")\n                        }\n                  }\n            )\n        ;\n\n    private readonly Dictionary<string, string> ColumnMappings = CacheUtil.GetTypeProperties(typeof(TEntity))\n         .Where(_ => !_.IsDefined(typeof(NotMappedAttribute)))\n         .Select(p => new KeyValuePair<string, string>(p.GetColumnName(), p.Name))\n         .ToDictionary(p => p.Key, p => p.Value);\n\n    private readonly string SelectColumnsString = CacheUtil.GetTypeProperties(typeof(TEntity))\n        .Where(_ => !_.IsDefined(typeof(NotMappedAttribute))).Select(_ => $\"{_.GetColumnName()} AS {_.Name}\").StringJoin(\",\");\n\n    private readonly Lazy<Dictionary<string, string>> InsertColumnMappings = new(() => CacheUtil.GetTypeProperties(typeof(TEntity))\n              .Where(_ => !_.IsDefined(typeof(NotMappedAttribute)) && !_.IsDefined(typeof(DatabaseGeneratedAttribute)))\n              .Select(p => new KeyValuePair<string, string>(p.GetColumnName(), p.Name))\n              .ToDictionary(_ => _.Key, _ => _.Value));\n\n    private readonly string _tableName = typeof(TEntity).GetCustomAttribute<TableAttribute>()?.Name ?? typeof(TEntity).Name;\n\n    #endregion TODO: Cache External\n\n    protected readonly Lazy<DbConnection> _dbConnection = new(dbConnectionFunc, true);\n\n    public virtual int Count(Expression<Func<TEntity, bool>> whereExpression)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n\n        var sql = $@\"\nSELECT COUNT(1) FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.ExecuteScalarTo<int>(sql, whereSql.Parameters);\n    }\n\n    public virtual Task<int> CountAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n\n        var sql = $@\"\nSELECT COUNT(1) FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.ExecuteScalarToAsync<int>(sql, whereSql.Parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual long LongCount(Expression<Func<TEntity, bool>> whereExpression)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n\n        var sql = $@\"\nSELECT COUNT(1) FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.ExecuteScalarTo<long>(sql, whereSql.Parameters);\n    }\n\n    public virtual Task<long> LongCountAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n\n        var sql = $@\"\nSELECT COUNT(1) FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.ExecuteScalarToAsync<long>(sql, whereSql.Parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual bool Exist(Expression<Func<TEntity, bool>> whereExpression)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"SELECT CAST(IIF(EXISTS (SELECT TOP(1) 1 FROM {_tableName} {whereSql.SqlText}), 1, 0) AS BIT)\";\n        return _dbConnection.Value.ExecuteScalarTo<bool>(sql, whereSql.Parameters);\n    }\n\n    public virtual Task<bool> ExistAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"SELECT CAST(IIF(EXISTS (SELECT TOP(1) 1 FROM {_tableName} {whereSql.SqlText}), 1, 0) AS BIT)\";\n        return _dbConnection.Value.ExecuteScalarToAsync<bool>(sql, whereSql.Parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual TEntity? Fetch(Expression<Func<TEntity, bool>> whereExpression)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nSELECT TOP(1) {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.Fetch<TEntity>(sql, whereSql.Parameters);\n    }\n\n    public virtual Task<TEntity?> FetchAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nSELECT TOP 1 {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.FetchAsync<TEntity>(sql, whereSql.Parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual TEntity? Fetch<TProperty>(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nSELECT TOP(1) {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\nORDER BY {GetColumnName(orderByExpression.GetMemberName())}  {(ascending ? \"\" : \"DESC\")}\n\";\n        return _dbConnection.Value.Fetch<TEntity>(sql, whereSql.Parameters);\n    }\n\n    public virtual Task<TEntity?> FetchAsync<TProperty>(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nSELECT TOP(1) {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\nORDER BY {GetColumnName(orderByExpression.GetMemberName())}  {(ascending ? \"\" : \"DESC\")}\n\";\n        return _dbConnection.Value.FetchAsync<TEntity>(sql, whereSql.Parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual List<TEntity> Select(Expression<Func<TEntity, bool>> whereExpression)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nSELECT {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.Select<TEntity>(sql, whereSql.Parameters).ToList();\n    }\n\n    public virtual Task<List<TEntity>> SelectAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nSELECT {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.SelectAsync<TEntity>(sql, whereSql.Parameters, cancellationToken: cancellationToken).ContinueWith(r => r.Result.ToList(), cancellationToken);\n    }\n\n    public virtual List<TEntity> Select<TProperty>(int count, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nSELECT TOP({count}) {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\nORDER BY {GetColumnName(orderByExpression.GetMemberName())} {(ascending ? \"\" : \"DESC\")}\n\";\n        return _dbConnection.Value.Select<TEntity>(sql, whereSql.Parameters).ToList();\n    }\n\n    public virtual Task<List<TEntity>> SelectAsync<TProperty>(int count, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nSELECT TOP({count}) {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\nORDER BY {GetColumnName(orderByExpression.GetMemberName())} {(ascending ? \"\" : \"DESC\")}\n\";\n        return _dbConnection.Value.SelectAsync<TEntity>(sql, whereSql.Parameters, cancellationToken: cancellationToken).ContinueWith(_ => _.Result.ToList(), cancellationToken);\n    }\n\n    public virtual IPagedListResult<TEntity> Paged<TProperty>(int pageNumber, int pageSize, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        if (pageNumber <= 0)\n        {\n            pageNumber = 1;\n        }\n        if (pageSize <= 0)\n        {\n            pageSize = 10;\n        }\n        var sql = $@\"\nSELECT COUNT(1) FROM {_tableName}\n{whereSql.SqlText}\n\";\n        var total = _dbConnection.Value.ExecuteScalarTo<int>(sql, whereSql.Parameters);\n        if (total == 0)\n        {\n            return PagedListResult<TEntity>.Empty;\n        }\n\n        var offset = (pageNumber - 1) * pageSize;\n\n        sql = $@\"\nSELECT {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\nORDER BY {GetColumnName(orderByExpression.GetMemberName())}{(ascending ? \"\" : \" DESC\")}\nOFFSET {offset} ROWS\nFETCH NEXT {pageSize} ROWS ONLY\n\";\n\n        return _dbConnection.Value.Select<TEntity>(sql, whereSql.Parameters).ToPagedList(pageNumber, pageSize, total);\n    }\n\n    public virtual async Task<IPagedListResult<TEntity>> PagedAsync<TProperty>(int pageNumber, int pageSize, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        if (pageNumber <= 0)\n        {\n            pageNumber = 1;\n        }\n        if (pageSize <= 0)\n        {\n            pageSize = 10;\n        }\n        var sql = $@\"\nSELECT COUNT(1) FROM {_tableName}\n{whereSql.SqlText}\n\";\n        var total = await _dbConnection.Value.ExecuteScalarToAsync<int>(sql, whereSql.Parameters, cancellationToken: cancellationToken);\n        if (total == 0)\n        {\n            return PagedListResult<TEntity>.Empty;\n        }\n\n        var offset = (pageNumber - 1) * pageSize;\n\n        sql = $@\"\nSELECT {SelectColumnsString} FROM {_tableName}\n{whereSql.SqlText}\nORDER BY {GetColumnName(orderByExpression.GetMemberName())}{(ascending ? \"\" : \" DESC\")}\nOFFSET {offset} ROWS\nFETCH NEXT {pageSize} ROWS ONLY\n\";\n\n        return (await _dbConnection.Value.SelectAsync<TEntity>(sql, whereSql.Parameters, cancellationToken: cancellationToken)).ToPagedList(pageNumber, pageSize, total);\n    }\n\n    public virtual int Insert(TEntity entity)\n    {\n        var paramDictionary = new Dictionary<string, object?>();\n        var sqlBuilder = new StringBuilder($@\"INSERT INTO {_tableName}\");\n        sqlBuilder.AppendLine();\n        sqlBuilder.AppendLine(\"(\");\n        sqlBuilder.AppendLine($\"{InsertColumnMappings.Value.Keys.Select(_ => _).StringJoin($\",{Environment.NewLine}\")}\");\n        sqlBuilder.AppendLine(\")\");\n        sqlBuilder.AppendLine(\"VALUES\");\n        sqlBuilder.AppendLine(\"(\");\n        sqlBuilder.AppendLine($\"{InsertColumnMappings.Value.Keys.Select(_ => $\"@{_}\").StringJoin($\",{Environment.NewLine}\")}\");\n        sqlBuilder.AppendLine(\")\");\n        foreach (var field in InsertColumnMappings.Value.Keys)\n        {\n            paramDictionary.Add($\"{field}\", Guard.NotNull(typeof(TEntity).GetProperty(InsertColumnMappings.Value[field])).GetValue(entity));\n        }\n        var sql = sqlBuilder.ToString();\n\n        return _dbConnection.Value.Execute(sql, paramDictionary);\n    }\n\n    public virtual Task<int> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)\n    {\n        var paramDictionary = new Dictionary<string, object?>();\n\n        var sqlBuilder = new StringBuilder($@\"INSERT INTO {_tableName}\");\n        sqlBuilder.AppendLine(\"(\");\n        sqlBuilder.AppendLine($\"{InsertColumnMappings.Value.Keys.Select(_ => _).StringJoin($\",{Environment.NewLine}\")}\");\n        sqlBuilder.AppendLine(\")\");\n        sqlBuilder.AppendLine(\"VALUES (\");\n        sqlBuilder.AppendLine($\"{InsertColumnMappings.Value.Keys.Select(_ => $\"@{_}\").StringJoin($\",{Environment.NewLine}\")}\");\n        foreach (var field in InsertColumnMappings.Value.Keys)\n        {\n            paramDictionary.Add($\"{field}\", Guard.NotNull(typeof(TEntity).GetProperty(InsertColumnMappings.Value[field])).GetValue(entity));\n        }\n\n        sqlBuilder.AppendLine(\")\");\n        return _dbConnection.Value.ExecuteAsync(sqlBuilder.ToString(), paramDictionary, cancellationToken: cancellationToken);\n    }\n\n    public virtual int Insert(IEnumerable<TEntity> entities)\n    {\n        var count = entities.Count();\n        if (count == 0)\n        {\n            return 0;\n        }\n        if (count > 1000)\n        {\n            return -1; // too large, not supported\n        }\n        var paramDictionary = new Dictionary<string, object?>();\n        var sqlBuilder = new StringBuilder($@\"INSERT INTO {_tableName}\");\n        sqlBuilder.AppendLine(\"(\");\n        sqlBuilder.AppendLine($\"{InsertColumnMappings.Value.Keys.Select(_ => _).StringJoin($\",{Environment.NewLine}\")}\");\n        sqlBuilder.AppendLine(\")\");\n        sqlBuilder.AppendLine(\"VALUES\");\n\n        foreach (var entity in entities)\n        {\n            var i = 0;\n            sqlBuilder.AppendLine();\n            sqlBuilder.AppendLine(\"(\");\n            sqlBuilder.AppendLine($\"{InsertColumnMappings.Value.Keys.Select(_ => $\"@{_}_{i}\").StringJoin($\",{Environment.NewLine}\")}\");\n            foreach (var field in InsertColumnMappings.Value.Keys)\n            {\n                paramDictionary.Add($\"{field}_{i}\", Guard.NotNull(typeof(TEntity).GetProperty(InsertColumnMappings.Value[field])).GetValue(entity));\n            }\n            sqlBuilder.Append(\"),\");\n            i++;\n        }\n        sqlBuilder.Remove(sqlBuilder.Length - 2, 1);\n\n        return _dbConnection.Value.Execute(sqlBuilder.ToString(), paramDictionary);\n    }\n\n    public virtual Task<int> InsertAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)\n    {\n        var count = entities.Count();\n        if (count == 0)\n        {\n            return Task.FromResult(0);\n        }\n        if (count > 1000)\n        {\n            return Task.FromResult(-1); // too large, not supported\n        }\n        var paramDictionary = new Dictionary<string, object?>();\n        var sqlBuilder = new StringBuilder($@\"INSERT INTO {_tableName}\");\n        sqlBuilder.AppendLine(\"(\");\n        sqlBuilder.AppendLine($\"{InsertColumnMappings.Value.Keys.Select(_ => _).StringJoin($\",{Environment.NewLine}\")}\");\n        sqlBuilder.AppendLine(\")\");\n        sqlBuilder.AppendLine(\"VALUES\");\n\n        foreach (var entity in entities)\n        {\n            var i = 0;\n            sqlBuilder.AppendLine();\n            sqlBuilder.AppendLine(\"(\");\n            sqlBuilder.AppendLine($\"{InsertColumnMappings.Value.Keys.Select(_ => $\"@{_}_{i}\").StringJoin($\",{Environment.NewLine}\")}\");\n            foreach (var field in InsertColumnMappings.Value.Keys)\n            {\n                paramDictionary.Add($\"{field}_{i}\", Guard.NotNull(typeof(TEntity).GetProperty(InsertColumnMappings.Value[field])).GetValue(entity));\n            }\n            sqlBuilder.Append(\"),\");\n            i++;\n        }\n\n        sqlBuilder.Remove(sqlBuilder.Length - 2, 1);\n\n        return _dbConnection.Value.ExecuteAsync(sqlBuilder.ToString(), paramDictionary, cancellationToken: cancellationToken);\n    }\n\n    public virtual int Update<TProperty>(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> propertyExpression, object? value)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var propertyName = propertyExpression.GetMemberName();\n        var sql = $@\"\nUPDATE {_tableName}\nSET {GetColumnName(propertyName)} = @set_{propertyName}\n{whereSql.SqlText}\n\";\n        whereSql.Parameters.Add($\"set_{propertyName}\", value);\n        return _dbConnection.Value.Execute(sql, whereSql.Parameters);\n    }\n\n    public virtual Task<int> UpdateAsync<TProperty>(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> propertyExpression, object? value, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var propertyName = propertyExpression.GetMemberName();\n        var sql = $@\"\nUPDATE {_tableName}\nSET {GetColumnName(propertyName)} = @set_{propertyName}\n{whereSql.SqlText}\n\";\n        whereSql.Parameters.Add($\"set_{propertyName}\", value);\n        return _dbConnection.Value.ExecuteAsync(sql, whereSql.Parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual int Update(Expression<Func<TEntity, bool>> whereExpression, IDictionary<string, object?> propertyValues)\n    {\n        if (propertyValues.IsNullOrEmpty())\n        {\n            return 0;\n        }\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nUPDATE {_tableName}\nSET {propertyValues.Keys.Select(p => $\"{GetColumnName(p)}=@set_{p}\").StringJoin($\",{Environment.NewLine}\")}\n{whereSql.SqlText}\n\";\n        foreach (var propertyValue in propertyValues)\n        {\n            whereSql.Parameters.Add($\"set_{propertyValue.Key}\", propertyValue.Value);\n        }\n        return _dbConnection.Value.Execute(sql, whereSql.Parameters);\n    }\n\n    public virtual int Update(TEntity entity, params Expression<Func<TEntity, object?>>[] propertyExpressions)\n    {\n        if (propertyExpressions.IsNullOrEmpty())\n        {\n            return UpdateWithout(entity, Array.Empty<string>());\n        }\n        //\n        var keyEntries = PrimaryKeyColumns.Value\n            .ToDictionary(p => p.Key, p => new KeyEntry()\n            {\n                PropertyName = p.Key,\n                ColumnName = p.Value,\n                Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n            });\n        if (keyEntries.Count == 0)\n        {\n            return -1;\n        }\n        //...\n        var updateCols = propertyExpressions.Select(p => p.GetMemberName()).ToArray();\n        var sql = $@\"\nUPDATE {_tableName}\nSET {updateCols.Select(p => $\"{GetColumnName(p)} = @set_{p}\").StringJoin(\", \")}\nWHERE {keyEntries.Select(k => $\"{k.Value.ColumnName} = @key_{k.Key}\")}\n\";\n        var parameters = new Dictionary<string, object?>();\n        foreach (var col in updateCols)\n        {\n            parameters[$\"set_{col}\"] = GetColumnName(col);\n        }\n        foreach (var entry in keyEntries)\n        {\n            parameters[$\"key_{entry.Key}\"] = entry.Value.Value;\n        }\n        return _dbConnection.Value.Execute(sql, parameters);\n    }\n\n    public virtual int UpdateWithout(TEntity entity, params Expression<Func<TEntity, object?>>[] propertyExpressions)\n    {\n        Guard.NotNull(propertyExpressions, nameof(propertyExpressions));\n\n        var keyEntries = PrimaryKeyColumns.Value\n            .ToDictionary(p => p.Key, p => new KeyEntry()\n            {\n                PropertyName = p.Key,\n                ColumnName = p.Value,\n                Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n            });\n        if (keyEntries.Count == 0)\n        {\n            return -1;\n        }\n        //...\n        var updateWithoutCols = propertyExpressions.Select(p => p.GetMemberName()).ToArray();\n        var updateCols = ColumnMappings.Keys\n            .Where(c => !updateWithoutCols.Contains(c) && !keyEntries.ContainsKey(c))\n            .ToArray();\n        if (updateCols.Length == 0)\n            return 0;\n\n        var sql = $@\"\nUPDATE {_tableName}\nSET {updateCols.Select(p => $\"{GetColumnName(p)} = @set_{p}\").StringJoin(\", \")}\nWHERE {keyEntries.Select(k => $\"{k.Value.ColumnName} = @key_{k.Key}\")}\n\";\n        var parameters = new Dictionary<string, object?>();\n        foreach (var col in updateCols)\n        {\n            parameters[$\"set_{col}\"] = GetColumnName(col);\n        }\n        foreach (var entry in keyEntries)\n        {\n            parameters[$\"key_{entry.Key}\"] = entry.Value.Value;\n        }\n        return _dbConnection.Value.Execute(sql, parameters);\n    }\n\n    public virtual int Update(TEntity entity, params string[] propertyNames)\n    {\n        if (propertyNames.IsNullOrEmpty())\n        {\n            return UpdateWithout(entity, Array.Empty<string>());\n        }\n        //\n        var keyEntries = PrimaryKeyColumns.Value\n            .ToDictionary(p => p.Key, p => new KeyEntry()\n            {\n                PropertyName = p.Key,\n                ColumnName = p.Value,\n                Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n            });\n        if (keyEntries.Count == 0)\n        {\n            return -1;\n        }\n        //...\n        var updateCols = propertyNames;\n        var sql = $@\"\nUPDATE {_tableName}\nSET {updateCols.Select(p => $\"{GetColumnName(p)} = @set_{p}\").StringJoin(\", \")}\nWHERE {keyEntries.Select(k => $\"{k.Value.ColumnName} = @key_{k.Key}\")}\n\";\n        var parameters = new Dictionary<string, object?>();\n        foreach (var col in updateCols)\n        {\n            parameters[$\"set_{col}\"] = GetColumnName(col);\n        }\n        foreach (var entry in keyEntries)\n        {\n            parameters[$\"key_{entry.Key}\"] = entry.Value.Value;\n        }\n        return _dbConnection.Value.Execute(sql, parameters);\n    }\n\n    public virtual int UpdateWithout(TEntity entity, params string[]? propertyNames)\n    {\n        var keyEntries = PrimaryKeyColumns.Value\n            .ToDictionary(p => p.Key, p => new KeyEntry()\n            {\n                PropertyName = p.Key,\n                ColumnName = p.Value,\n                Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n            });\n        if (keyEntries.Count == 0)\n        {\n            return -1;\n        }\n        //...\n        var updateWithoutCols = propertyNames ?? [];\n        var updateCols = ColumnMappings.Keys\n            .Where(c => !updateWithoutCols.Contains(c) && !keyEntries.ContainsKey(c))\n            .ToArray();\n        if (updateCols.Length == 0)\n            return 0;\n\n        var sql = $@\"\nUPDATE {_tableName}\nSET {updateCols.Select(p => $\"{GetColumnName(p)} = @set_{p}\").StringJoin(\", \")}\nWHERE {keyEntries.Select(k => $\"{k.Value.ColumnName} = @key_{k.Key}\")}\n\";\n        var parameters = new Dictionary<string, object?>();\n        foreach (var col in updateCols)\n        {\n            parameters[$\"set_{col}\"] = GetColumnName(col);\n        }\n        foreach (var entry in keyEntries)\n        {\n            parameters[$\"key_{entry.Key}\"] = entry.Value.Value;\n        }\n        return _dbConnection.Value.Execute(sql, parameters);\n    }\n\n    public virtual Task<int> UpdateWithoutAsync(TEntity entity, string[] propertyNames, CancellationToken cancellationToken = default)\n    {\n        var keyEntries = PrimaryKeyColumns.Value\n            .ToDictionary(p => p.Key, p => new KeyEntry()\n            {\n                PropertyName = p.Key,\n                ColumnName = p.Value,\n                Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n            });\n        if (keyEntries.Count == 0)\n        {\n            return Task.FromResult(-1);\n        }\n\n        //...\n        var updateWithoutCols = propertyNames.Distinct().ToArray();\n        var updateCols = ColumnMappings.Keys\n            .Where(c => !updateWithoutCols.Contains(c) && !keyEntries.ContainsKey(c))\n            .ToArray();\n        if (updateCols.Length == 0)\n            return Task.FromResult(0);\n\n        var sql = $@\"\nUPDATE {_tableName}\nSET {updateCols.Select(p => $\"{GetColumnName(p)} = @set_{p}\").StringJoin(\", \")}\nWHERE {keyEntries.Select(k => $\"{k.Value.ColumnName} = @key_{k.Key}\")}\n\";\n        var parameters = new Dictionary<string, object?>();\n        foreach (var col in updateCols)\n        {\n            parameters[$\"set_{col}\"] = GetColumnName(col);\n        }\n        foreach (var entry in keyEntries)\n        {\n            parameters[$\"key_{entry.Key}\"] = entry.Value.Value;\n        }\n        return _dbConnection.Value.ExecuteAsync(sql, paramInfo: parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual Task<int> UpdateAsync(TEntity entity, Expression<Func<TEntity, object?>>[] propertyExpressions, CancellationToken cancellationToken = default)\n    {\n        Guard.NotNull(propertyExpressions, nameof(propertyExpressions));\n\n        if (propertyExpressions.Length == 0)\n        {\n            return UpdateWithoutAsync(entity, Array.Empty<string>(), cancellationToken);\n        }\n        //\n        var keyEntries = PrimaryKeyColumns.Value\n            .ToDictionary(p => p.Key, p => new KeyEntry()\n            {\n                PropertyName = p.Key,\n                ColumnName = p.Value,\n                Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n            });\n        if (keyEntries.Count == 0)\n        {\n            return Task.FromResult(-1);\n        }\n        //...\n        var updateCols = propertyExpressions.Select(p => p.GetMemberName()).ToArray();\n        var sql = $@\"\nUPDATE {_tableName}\nSET {updateCols.Select(p => $\"{GetColumnName(p)} = @set_{p}\").StringJoin(\", \")}\nWHERE {keyEntries.Select(k => $\"{k.Value.ColumnName} = @key_{k.Key}\")}\n\";\n        var parameters = new Dictionary<string, object?>();\n        foreach (var col in updateCols)\n        {\n            parameters[$\"set_{col}\"] = GetColumnName(col);\n        }\n        foreach (var entry in keyEntries)\n        {\n            parameters[$\"key_{entry.Key}\"] = entry.Value.Value;\n        }\n        return _dbConnection.Value.ExecuteAsync(sql, parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual Task<int> UpdateWithoutAsync(TEntity entity, Expression<Func<TEntity, object?>>[] propertyExpressions,\n        CancellationToken cancellationToken = default)\n    {\n        var keyEntries = PrimaryKeyColumns.Value\n          .ToDictionary(p => p.Key, p => new KeyEntry()\n          {\n              PropertyName = p.Key,\n              ColumnName = p.Value,\n              Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n          });\n        if (keyEntries.Count == 0)\n        {\n            return Task.FromResult(-1);\n        }\n        //...\n        var updateWithoutCols = propertyExpressions.Select(x => x.GetMemberName()).Distinct().ToArray();\n        var updateCols = ColumnMappings.Keys\n            .Where(c => !updateWithoutCols.Contains(c) && !keyEntries.ContainsKey(c))\n            .ToArray();\n        if (updateCols.Length == 0)\n            return Task.FromResult(0);\n\n        var sql = $@\"\nUPDATE {_tableName}\nSET {updateCols.Select(p => $\"{GetColumnName(p)} = @set_{p}\").StringJoin(\", \")}\nWHERE {keyEntries.Select(k => $\"{k.Value.ColumnName} = @key_{k.Key}\")}\n\";\n        var parameters = new Dictionary<string, object?>();\n        foreach (var col in updateCols)\n        {\n            parameters[$\"set_{col}\"] = GetColumnName(col);\n        }\n        foreach (var entry in keyEntries)\n        {\n            parameters[$\"key_{entry.Key}\"] = entry.Value.Value;\n        }\n        return _dbConnection.Value.ExecuteAsync(sql, paramInfo: parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual Task<int> UpdateAsync(TEntity entity, string[] propertyNames, CancellationToken cancellationToken = default)\n    {\n        Guard.NotNull(propertyNames, nameof(propertyNames));\n\n        if (propertyNames.Length == 0)\n        {\n            return UpdateWithoutAsync(entity, Array.Empty<string>(), cancellationToken);\n        }\n        //\n        var keyEntries = PrimaryKeyColumns.Value\n            .ToDictionary(p => p.Key, p => new KeyEntry()\n            {\n                PropertyName = p.Key,\n                ColumnName = p.Value,\n                Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n            });\n        if (keyEntries.Count == 0)\n        {\n            return Task.FromResult(-1);\n        }\n        //...\n        var updateCols = propertyNames;\n        var sql = $@\"\nUPDATE {_tableName}\nSET {updateCols.Select(p => $\"{GetColumnName(p)} = @set_{p}\").StringJoin(\", \")}\nWHERE {keyEntries.Select(k => $\"{k.Value.ColumnName} = @key_{k.Key}\")}\n\";\n        var parameters = new Dictionary<string, object?>();\n        foreach (var col in updateCols)\n        {\n            parameters[$\"set_{col}\"] = GetColumnName(col);\n        }\n        foreach (var entry in keyEntries)\n        {\n            parameters[$\"key_{entry.Key}\"] = entry.Value.Value;\n        }\n        return _dbConnection.Value.ExecuteAsync(sql, parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual Task<int> UpdateAsync(Expression<Func<TEntity, bool>> whereExpression,\n        IDictionary<string, object?>? propertyValues, CancellationToken cancellationToken = default)\n    {\n        if (propertyValues is null || propertyValues.Count == 0)\n        {\n            return Task.FromResult(0);\n        }\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nUPDATE {_tableName}\nSET {propertyValues.Keys.Select(p => $\"{GetColumnName(p)}=@set_{p}\").StringJoin($\",{Environment.NewLine}\")}\n{whereSql.SqlText}\n\";\n        foreach (var propertyValue in propertyValues)\n        {\n            whereSql.Parameters.Add($\"set_{propertyValue.Key}\", propertyValue.Value);\n        }\n        return _dbConnection.Value.ExecuteAsync(sql, whereSql.Parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual int Delete(Expression<Func<TEntity, bool>> whereExpression)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nDELETE FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.Execute(sql, whereSql.Parameters);\n    }\n\n    public virtual int Delete(TEntity entity)\n    {\n        var keyEntries = PrimaryKeyColumns.Value\n            .ToDictionary(p => p.Key, p => new KeyEntry()\n            {\n                PropertyName = p.Key,\n                ColumnName = p.Value,\n                Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n            });\n        if (keyEntries.Count == 0)\n        {\n            return -1;\n        }\n\n        var sql = $@\"\nDELETE FROM {_tableName}\nWHERE {keyEntries.Select(x => $\"{x.Value.ColumnName} = @{x.Key}\").StringJoin(\" AND \")}\n\";\n        return _dbConnection.Value.Execute(sql,\n            keyEntries.ToDictionary(\n                    x => x.Key,\n                    x => x.Value.Value));\n    }\n\n    public virtual Task<int> DeleteAsync(Expression<Func<TEntity, bool>> whereExpression, CancellationToken cancellationToken = default)\n    {\n        var whereSql = SqlExpressionParser.ParseWhereExpression(whereExpression, ColumnMappings);\n        var sql = $@\"\nDELETE FROM {_tableName}\n{whereSql.SqlText}\n\";\n        return _dbConnection.Value.ExecuteAsync(sql, whereSql.Parameters, cancellationToken: cancellationToken);\n    }\n\n    public virtual async Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)\n    {\n        var keyEntries = PrimaryKeyColumns.Value\n            .ToDictionary(p => p.Key, p => new KeyEntry()\n            {\n                PropertyName = p.Key,\n                ColumnName = p.Value,\n                Value = Guard.NotNull(typeof(TEntity).GetProperty(p.Key)).GetValue(entity)\n            });\n        if (keyEntries.Count == 0)\n        {\n            return -1;\n        }\n\n        var sql = $@\"\nDELETE FROM {_tableName}\nWHERE {keyEntries.Select(x => $\"{x.Value.ColumnName} = @{x.Key}\").StringJoin(\" AND \")}\n\";\n        return await _dbConnection.Value.ExecuteAsync(sql,\n            keyEntries.ToDictionary(\n                x => x.Key,\n                x => x.Value.Value), cancellationToken: cancellationToken);\n    }\n\n    public virtual int Execute(string sqlStr, object? param = null)\n    => _dbConnection.Value.Execute(sqlStr, paramInfo: param);\n\n    public virtual Task<int> ExecuteAsync(string sqlStr, object? param = null, CancellationToken cancellationToken = default)\n    => _dbConnection.Value.ExecuteAsync(sqlStr, paramInfo: param, cancellationToken: cancellationToken);\n\n    public virtual TResult ExecuteScalar<TResult>(string sqlStr, object? param = null)\n\n    => _dbConnection.Value.ExecuteScalarTo<TResult>(sqlStr, paramInfo: param);\n\n    public virtual Task<TResult> ExecuteScalarAsync<TResult>(string sqlStr, object? param = null, CancellationToken cancellationToken = default)\n\n    => _dbConnection.Value.ExecuteScalarToAsync<TResult>(sqlStr, paramInfo: param, cancellationToken: cancellationToken);\n\n    private string GetColumnName(string propertyName)\n    {\n        return ColumnMappings.TryGetValue(propertyName, out var colName)\n            ? colName : propertyName;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Data/RepositoryExtension.cs",
    "content": "﻿using System.Linq.Expressions;\n\nnamespace WeihanLi.Common.Data;\n\npublic static class RepositoryExtension\n{\n    public static int Count<TEntity>(this IReadOnlyRepository<TEntity> repository) => repository.Count(x => true);\n\n    public static Task<int> CountAsync<TEntity>(this IReadOnlyRepository<TEntity> repository, CancellationToken cancellationToken = default) =>\n        repository.CountAsync(x => true, cancellationToken);\n\n    public static long LongCount<TEntity>(this IReadOnlyRepository<TEntity> repository) => repository.LongCount(x => true);\n\n    public static Task<long> LongCountAsync<TEntity>(this IReadOnlyRepository<TEntity> repository, CancellationToken cancellationToken = default) =>\n        repository.LongCountAsync(x => true, cancellationToken);\n\n    public static TEntity? Fetch<TEntity>(this IReadOnlyRepository<TEntity> repository) => repository.Fetch(x => true);\n\n    public static Task<TEntity?> FetchAsync<TEntity>(this IReadOnlyRepository<TEntity> repository, CancellationToken cancellationToken = default) =>\n        repository.FetchAsync(x => true, cancellationToken);\n\n    public static TEntity? Fetch<TEntity, TProperty>(this IReadOnlyRepository<TEntity> repository, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false) => repository.Fetch(x => true, orderByExpression, ascending);\n\n    public static Task<TEntity?> FetchAsync<TEntity, TProperty>(this IReadOnlyRepository<TEntity> repository, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false, CancellationToken cancellationToken = default) =>\n        repository.FetchAsync(x => true, orderByExpression, ascending, cancellationToken);\n\n    public static List<TEntity> GetAll<TEntity>(this IReadOnlyRepository<TEntity> repository) =>\n        repository.Select(x => true);\n\n    public static Task<List<TEntity>> GetAllAsync<TEntity>(this IReadOnlyRepository<TEntity> repository, CancellationToken cancellationToken = default) =>\n        repository.SelectAsync(x => true, cancellationToken);\n\n    public static List<TEntity> Top<TEntity, TProperty>(this IReadOnlyRepository<TEntity> repository, int count, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false) =>\n        repository.Select(count, x => true, orderByExpression, ascending);\n\n    public static Task<List<TEntity>> TopAsync<TEntity, TProperty>(this IReadOnlyRepository<TEntity> repository, int count, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false, CancellationToken cancellationToken = default) =>\n        repository.SelectAsync(count, x => true, orderByExpression, ascending, cancellationToken);\n\n    public static List<TEntity> Top<TEntity, TProperty>(this IReadOnlyRepository<TEntity> repository, int count, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false) =>\n        repository.Select(count, whereExpression, orderByExpression, ascending);\n\n    public static Task<List<TEntity>> TopAsync<TEntity, TProperty>(this IReadOnlyRepository<TEntity> repository, int count, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = false, CancellationToken cancellationToken = default) =>\n        repository.SelectAsync(count, whereExpression, orderByExpression, ascending, cancellationToken);\n\n    public static Task<int> UpdateAsync<TEntity>(this IRepository<TEntity> repository,\n        TEntity entity, params string[] propertyNames) where TEntity : class\n        => repository.UpdateAsync(entity, propertyNames);\n\n    public static Task<int> UpdateAsync<TEntity>(this IRepository<TEntity> repository,\n        TEntity entity,\n        params Expression<Func<TEntity, object?>>[] propertyExpressions)\n        where TEntity : class\n        => repository.UpdateAsync(entity, propertyExpressions);\n\n    public static Task<int> UpdateWithoutAsync<TEntity>(this IRepository<TEntity> repository,\n        TEntity entity, params string[] propertyNames) where TEntity : class\n        => repository.UpdateWithoutAsync(entity, propertyNames);\n\n    public static Task<int> UpdateWithoutAsync<TEntity>(this IRepository<TEntity> repository,\n        TEntity entity,\n        params Expression<Func<TEntity, object?>>[] propertyExpressions)\n        where TEntity : class\n        => repository.UpdateWithoutAsync(entity, propertyExpressions);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Data/SqlExpressionParser.cs",
    "content": "﻿using System.Linq.Expressions;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Data;\n\ninternal static partial class SqlExpressionParser\n{\n    internal static SqlParseResult ParseWhereExpression(Expression? exp, IDictionary<string, string>? columnMappings)\n    {\n        var sqlText = string.Empty;\n        var dic = new Dictionary<string, object?>();\n        if (exp != null && exp.NodeType == ExpressionType.Lambda && exp is LambdaExpression)\n        {\n            var condition = ParseExpression(exp, columnMappings);\n            if (condition.IsNotNullOrWhiteSpace())\n            {\n                sqlText = $\"WHERE {condition}\";\n            }\n        }\n        return new SqlParseResult(sqlText, dic);\n    }\n\n    public static string ParseExpression(Expression? exp, IDictionary<string, string>? columnMappings = null)\n    {\n        if (exp is LambdaExpression lambdaExpression)\n        {\n            return ParseExpression(lambdaExpression.Body, columnMappings);\n        }\n        if (exp is BinaryExpression binaryExpression)\n        {\n            var left = ParseExpression(binaryExpression.Left, columnMappings);\n            var @operator = GetExpressionOperatorString(binaryExpression);\n            var right = ParseExpression(binaryExpression.Right, columnMappings);\n            if (left == \"NULL\")\n            {\n                (left, right) = (right, left);\n            }\n            if (right == \"NULL\")\n            {\n                @operator = @operator == \"=\" ? \" IS \" : \" IS NOT \";\n            }\n            return $\"{left} {@operator} {right}\";\n        }\n        if (exp is MemberExpression memberExpression)\n        {\n            return ParseMemberExpression(memberExpression, columnMappings);\n        }\n        if (exp is MethodCallExpression methodCallExpression)\n        {\n            return ParseMethodCallExpression(methodCallExpression, columnMappings);\n        }\n        if (exp is ConstantExpression constantExpression)\n        {\n            return ParseConstantExpression(constantExpression);\n        }\n        if (exp is UnaryExpression unaryExpression)\n        {\n            return ParseExpression(unaryExpression.Operand, columnMappings);\n        }\n\n        return string.Empty;\n    }\n\n    private static string ParseMemberExpression(MemberExpression? exp, IDictionary<string, string>? columnMappings)\n    {\n        if (exp == null)\n        {\n            return string.Empty;\n        }\n        if (exp.Member.DeclaringType == typeof(DateTime))\n        {\n            return ParseDateTimeMemberAccess(exp, columnMappings);\n        }\n        if (exp.Member.DeclaringType == typeof(string))\n        {\n            return ParseStringMemberAccess(exp, columnMappings);\n        }\n        var memberName = exp.Member.Name;\n        if (columnMappings != null && columnMappings.Count > 0)\n        {\n            var mapping = columnMappings.FirstOrDefault(_ => _.Value == memberName);\n            if (mapping.Key.IsNotNullOrWhiteSpace())\n            {\n                return mapping.Key;\n            }\n        }\n        return memberName;\n    }\n\n    private static string GetExpressionOperatorString(BinaryExpression exp)\n    {\n        return exp.NodeType switch\n        {\n            ExpressionType.OrElse => \" OR \",\n            ExpressionType.Or => \"|\",\n            ExpressionType.AndAlso => \" AND \",\n            ExpressionType.And => \"&\",\n            ExpressionType.GreaterThan => \">\",\n            ExpressionType.GreaterThanOrEqual => \">=\",\n            ExpressionType.LessThan => \"<\",\n            ExpressionType.LessThanOrEqual => \"<=\",\n            ExpressionType.NotEqual => \"<>\",\n            ExpressionType.Add => \"+\",\n            ExpressionType.Subtract => \"-\",\n            ExpressionType.Multiply => \"*\",\n            ExpressionType.Divide => \"/\",\n            ExpressionType.Modulo => \"%\",\n            ExpressionType.Equal => \"=\",\n            _ => exp.NodeType.ToString()\n        };\n    }\n\n    private static string ParseConstantExpression(ConstantExpression exp)\n    {\n        if (exp.Value == null)\n        {\n            return \"IS NULL\";\n        }\n        if (exp.Value is string strVal)\n        {\n            return $\"N'{strVal.Replace(\"'\", \"''\")}'\";\n        }\n        if (exp.Value is bool bValue)\n        {\n            return bValue ? \"1\" : \"0\";\n        }\n        return $\"{exp.Value.ToString()?.Replace(\"'\", \"''\")}\";\n    }\n\n    private static string ParseMethodCallExpression(MethodCallExpression expression, IDictionary<string, string>? columnMappings)\n    {\n        if (expression.Object?.Type == typeof(string))\n        {\n            return ParseStringMethodCall(expression, columnMappings);\n        }\n        //\n        throw new NotImplementedException();\n    }\n}\n\ninternal class SqlParseResult(string sqlText, IDictionary<string, object?> parameters)\n{\n    public string SqlText { get; } = sqlText;\n\n    public IDictionary<string, object?> Parameters { get; } = parameters;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Data/SqlExpressionVisitor.cs",
    "content": "﻿using System.Linq.Expressions;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Data;\n\npublic class SqlExpressionVisitor(IDictionary<string, string>? columnMappings) : ExpressionVisitor\n{\n    private readonly Stack<string> _leaves = new();\n    private readonly IDictionary<string, string>? _columnMappings = columnMappings;\n\n    public string GetCondition() => _leaves.StringJoin(\" \");\n\n    protected override Expression VisitBinary(BinaryExpression node)\n    {\n        string GetOperator()\n        {\n            return node.NodeType switch\n            {\n                ExpressionType.LeftShift => \"<<\",\n                ExpressionType.RightShift => \">>\",\n                ExpressionType.AndAlso => \"AND\",\n                ExpressionType.OrElse => \"OR\",\n                ExpressionType.And => \"&\",\n                ExpressionType.Or => \"|\",\n                ExpressionType.GreaterThan => \">\",\n                ExpressionType.GreaterThanOrEqual => \">=\",\n                ExpressionType.LessThan => \"<\",\n                ExpressionType.LessThanOrEqual => \"<=\",\n                ExpressionType.Equal => \"=\",\n                ExpressionType.NotEqual => \"!=\",\n                ExpressionType.Add => \"+\",\n                ExpressionType.Subtract => \"-\",\n                ExpressionType.Multiply => \"*\",\n                ExpressionType.Divide => \"/\",\n                ExpressionType.Modulo => \"%\",\n                _ => node.NodeType.ToString()\n            };\n        }\n\n        // right\n        Visit(node.Right);\n\n        var rightMember = _leaves.Peek();\n        var oper = GetOperator();\n        if (rightMember == \"NULL\")\n        {\n            if (node.NodeType == ExpressionType.Equal)\n            {\n                _leaves.Push(\"IS\");\n            }\n            else if (node.NodeType == ExpressionType.NotEqual)\n            {\n                _leaves.Push(\"IS NOT\");\n            }\n            else\n            {\n                _leaves.Push(oper);\n            }\n        }\n        else\n        {\n            _leaves.Push(oper);\n        }\n\n        // left\n        Visit(node.Left);\n\n        return node;\n    }\n\n    protected override Expression VisitConstant(ConstantExpression node)\n    {\n        string GetResult()\n        {\n            if (node.Value == null)\n                return \"NULL\";\n\n            if (node.Value is string strVal)\n            {\n                return $\"N'{strVal.Replace(\"'\", \"''\")}'\";\n            }\n            if (node.Value is bool bValue)\n            {\n                return bValue ? \"1\" : \"0\";\n            }\n            return $\"{node.Value.ToString()?.Replace(\"'\", \"''\")}\";\n        }\n        _leaves.Push(GetResult());\n\n        return base.VisitConstant(node);\n    }\n\n    protected override Expression VisitMember(MemberExpression node)\n    {\n        string GetResult()\n        {\n            if (node.Member.DeclaringType == typeof(DateTime))\n            {\n                return SqlExpressionParser.ParseDateTimeMemberAccess(node, _columnMappings);\n            }\n            if (node.Member.DeclaringType == typeof(string))\n            {\n                return SqlExpressionParser.ParseStringMemberAccess(node, _columnMappings);\n            }\n            var memberName = node.Member.Name;\n            if (_columnMappings != null && _columnMappings.Count > 0)\n            {\n                var mapping = _columnMappings.FirstOrDefault(_ => _.Value == memberName);\n                if (mapping.Key.IsNotNullOrWhiteSpace())\n                {\n                    return mapping.Key;\n                }\n            }\n            return memberName;\n        }\n        _leaves.Push(GetResult());\n        return base.VisitMember(node);\n    }\n\n    protected override Expression VisitMethodCall(MethodCallExpression node)\n    {\n        string GetResult()\n        {\n            // TODO:完善 Method Call 解析\n            if (node.Object?.Type == typeof(string))\n            {\n                return SqlExpressionParser.ParseStringMethodCall(node, _columnMappings);\n            }\n            if (node.Object?.Type == typeof(DateTime))\n            {\n                return SqlExpressionParser.ParseDateTimeMethodCall(node, _columnMappings);\n            }\n            //\n            throw new NotImplementedException();\n        }\n        _leaves.Push(GetResult());\n        return base.VisitMethodCall(node);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Data/UnitOfWork.cs",
    "content": "﻿using System.Data;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Data;\n\n[CLSCompliant(false)]\npublic class UnitOfWork : IUnitOfWork, IDisposable\n{\n    private readonly IDbTransaction _dbTransaction;\n\n    protected IDbTransaction DbTransaction => _dbTransaction;\n\n    public UnitOfWork(IDbConnection dbConnection)\n    {\n        Guard.NotNull(dbConnection);\n        dbConnection.EnsureOpen();\n        _dbTransaction = dbConnection.BeginTransaction();\n    }\n\n    public UnitOfWork(IDbTransaction dbTransaction)\n    {\n        _dbTransaction = Guard.NotNull(dbTransaction);\n    }\n\n    public virtual void Commit() => _dbTransaction.Commit();\n\n    public virtual Task CommitAsync(CancellationToken cancellationToken = default)\n    {\n        _dbTransaction.Commit();\n        return Task.CompletedTask;\n    }\n\n    public virtual void Rollback() => _dbTransaction.Rollback();\n\n    public virtual Task RollbackAsync(CancellationToken cancellationToken = default)\n    {\n        _dbTransaction.Rollback();\n        return Task.CompletedTask;\n    }\n\n    public void Dispose()\n    {\n        Dispose(true);\n        GC.SuppressFinalize(this);\n    }\n\n    protected virtual void Dispose(bool disposing)\n    {\n        if (disposing)\n        {\n            // Cleanup\n            _dbTransaction.Dispose();\n        }\n    }\n\n    ~UnitOfWork()\n    {\n        Dispose(false);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/DependencyInjectionExtensions.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Common;\n\npublic static class DependencyInjectionExtensions\n{\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IEnumerable<object> GetServices(\n        this IServiceProvider provider,\n        Type serviceType)\n    {\n        Guard.NotNull(provider);\n        Guard.NotNull(serviceType);\n\n        return (IEnumerable<object>)Guard.NotNull(provider.GetService(typeof(IEnumerable<>).MakeGenericType(serviceType)));\n    }\n\n    /// <summary>\n    /// ResolveService\n    /// </summary>\n    /// <typeparam name=\"TService\">TService</typeparam>\n    /// <param name=\"serviceProvider\">serviceProvider</param>\n    /// <returns></returns>\n    public static TService? ResolveService<TService>(this IServiceProvider serviceProvider)\n   => (TService?)Guard.NotNull(serviceProvider).GetService(typeof(TService));\n\n    /// <summary>\n    /// ResolveRequiredService\n    /// throw exception if can not get a service instance\n    /// </summary>\n    /// <typeparam name=\"TService\">TService</typeparam>\n    /// <param name=\"serviceProvider\">serviceProvider</param>\n    /// <returns></returns>\n    public static TService ResolveRequiredService<TService>(this IServiceProvider serviceProvider)\n    {\n        Guard.NotNull(serviceProvider);\n        var serviceType = typeof(TService);\n        var svc = serviceProvider.GetService(serviceType);\n        if (null == svc)\n        {\n            throw new InvalidOperationException($\"service had not been registered, serviceType: {serviceType}\");\n        }\n        return (TService)svc;\n    }\n\n    /// <summary>\n    /// Resolve services\n    /// </summary>\n    /// <typeparam name=\"TService\">TService</typeparam>\n    /// <param name=\"serviceProvider\">serviceProvider</param>\n    /// <returns></returns>\n    public static IEnumerable<TService> ResolveServices<TService>(this IServiceProvider serviceProvider)\n        => Guard.NotNull(serviceProvider.ResolveService<IEnumerable<TService>>());\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/FromServiceAttribute.cs",
    "content": "﻿namespace WeihanLi.Common.DependencyInjection;\n\n[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]\npublic sealed class FromServiceAttribute : Attribute;\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/ServiceConstructorAttribute.cs",
    "content": "﻿namespace WeihanLi.Common.DependencyInjection;\n\n[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]\npublic sealed class ServiceConstructorAttribute : Attribute;\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/ServiceContainer.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing WeihanLi.Common.Services;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.DependencyInjection;\n\npublic interface IServiceContainer : IScope, IServiceProvider\n{\n    IServiceContainer CreateScope();\n}\n\ninternal sealed class ServiceContainer : IServiceContainer\n{\n    private readonly IReadOnlyList<ServiceDefinition> _services;\n\n    private readonly ConcurrentDictionary<ServiceKey, object?> _singletonInstances;\n\n    private readonly ConcurrentDictionary<ServiceKey, object?> _scopedInstances = new();\n    private readonly ConcurrentBag<object> _transientDisposables = [];\n\n    private sealed class ServiceKey(Type serviceType, ServiceDefinition definition) : IEquatable<ServiceKey>\n    {\n        public Type ServiceType { get; } = serviceType;\n\n        public Type ImplementType { get; } = definition.GetImplementType();\n\n        public bool Equals(ServiceKey? other)\n        {\n            return ServiceType == other?.ServiceType && ImplementType == other.ImplementType;\n        }\n\n        public override bool Equals(object? obj)\n        {\n            return Equals(obj as ServiceKey);\n        }\n\n        public override int GetHashCode()\n        {\n            var key = $\"{ServiceType.FullName}_{ImplementType.FullName}\";\n            return key.GetHashCode();\n        }\n    }\n\n    private readonly bool _isRootScope;\n\n    public ServiceContainer(IReadOnlyList<ServiceDefinition> serviceDefinitions)\n    {\n        _services = serviceDefinitions;\n\n        _isRootScope = true;\n        _singletonInstances = new ConcurrentDictionary<ServiceKey, object?>();\n    }\n\n    private ServiceContainer(ServiceContainer serviceContainer)\n    {\n        _isRootScope = false;\n        _singletonInstances = serviceContainer._singletonInstances;\n\n        _services = serviceContainer._services;\n        _scopedInstances = new ConcurrentDictionary<ServiceKey, object?>();\n    }\n\n    public IServiceContainer CreateScope()\n    {\n        return new ServiceContainer(this);\n    }\n\n    private bool _disposed;\n\n    public void Dispose()\n    {\n        if (_disposed)\n        {\n            return;\n        }\n\n        if (_isRootScope)\n        {\n            lock (_singletonInstances)\n            {\n                if (_disposed)\n                {\n                    return;\n                }\n\n                _disposed = true;\n                foreach (var instance in _singletonInstances.Values)\n                {\n                    (instance as IDisposable)?.Dispose();\n                }\n\n                foreach (var o in _transientDisposables)\n                {\n                    (o as IDisposable)?.Dispose();\n                }\n\n                _singletonInstances.Clear();\n            }\n        }\n        else\n        {\n            lock (_scopedInstances)\n            {\n                if (_disposed)\n                {\n                    return;\n                }\n\n                _disposed = true;\n                foreach (var instance in _scopedInstances.Values)\n                {\n                    (instance as IDisposable)?.Dispose();\n                }\n\n                foreach (var o in _transientDisposables)\n                {\n                    (o as IDisposable)?.Dispose();\n                }\n\n                _scopedInstances.Clear();\n            }\n        }\n    }\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    private object? EnrichObject(object? obj)\n    {\n        if (obj is not null)\n        {\n            var type = obj.GetType();\n            // PropertyInjection\n            foreach (var property in CacheUtil.GetTypeProperties(type)\n                .Where(x => x.IsDefined(typeof(FromServiceAttribute))))\n            {\n                property.GetValueSetter()?.Invoke(\n                    obj,\n                    GetService(property.PropertyType)\n                );\n            }\n        }\n        return obj;\n    }\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    private object? GetServiceInstance(Type serviceType, ServiceDefinition serviceDefinition)\n        => EnrichObject(GetServiceInstanceInternal(serviceType, serviceDefinition));\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    private object? GetServiceInstanceInternal(Type serviceType, ServiceDefinition serviceDefinition)\n    {\n        if (serviceDefinition.ImplementationInstance != null)\n            return serviceDefinition.ImplementationInstance;\n\n        if (serviceDefinition.ImplementationFactory != null)\n        {\n            return serviceDefinition.ImplementationFactory.Invoke(this);\n        }\n        var implementType = (serviceDefinition.ImplementType ?? serviceType);\n\n        if (implementType.IsInterface || implementType.IsAbstract)\n        {\n            throw new InvalidOperationException($\"invalid service registered, serviceType: {serviceType.FullName}, implementType: {serviceDefinition.ImplementType}\");\n        }\n\n        if (implementType.IsGenericType)\n        {\n            implementType = implementType.MakeGenericType(serviceType.GetGenericArguments());\n        }\n\n        var newFunc = CacheUtil.TypeNewFuncCache.GetOrAdd(implementType, (serviceContainer) =>\n        {\n            if (CacheUtil.TypeEmptyConstructorFuncCache.TryGetValue(implementType, out var emptyFunc))\n            {\n                return emptyFunc.Invoke();\n            }\n\n            var ctor = CacheUtil.TypeConstructorCache.GetOrAdd(implementType, t =>\n            {\n                var ctorInfos = t.GetConstructors(BindingFlags.Instance | BindingFlags.Public);\n                if (ctorInfos.Length == 0)\n                {\n                    return null;\n                }\n\n                ConstructorInfo ctorInfo;\n                if (ctorInfos.Length == 1)\n                {\n                    ctorInfo = ctorInfos[0];\n                }\n                else\n                {\n                    ctorInfo = ctorInfos.FirstOrDefault(x => x.IsDefined(typeof(ServiceConstructorAttribute)))\n                        ?? ctorInfos\n                        .OrderBy(c => c.GetParameters().Length)\n                        .First();\n                }\n\n                return ctorInfo;\n            }) ?? throw new InvalidOperationException(\n                    $\"service {serviceType.FullName} does not have any public constructors\");\n            var parameters = ctor.GetParameters();\n            if (parameters.Length == 0)\n            {\n                var func00 = Expression.Lambda<Func<object>>(Expression.New(ctor)).Compile();\n                CacheUtil.TypeEmptyConstructorFuncCache.TryAdd(implementType, func00);\n                return func00.Invoke();\n            }\n\n            var ctorParams = new object?[parameters.Length];\n            for (var index = 0; index < parameters.Length; index++)\n            {\n                var param = serviceContainer.GetService(parameters[index].ParameterType);\n                if (param == null && parameters[index].HasDefaultValue)\n                {\n                    param = parameters[index].DefaultValue;\n                }\n                ctorParams[index] = param;\n            }\n\n            var func = CacheUtil.TypeConstructorFuncCache.GetOrAdd(implementType, t =>\n            {\n                if (!CacheUtil.TypeConstructorCache.TryGetValue(t, out var ctorInfo) || ctorInfo is null)\n                {\n                    return null!;\n                }\n\n                var innerParameters = ctorInfo.GetParameters();\n                var parameterExpression = Expression.Parameter(typeof(object[]), \"arguments\"); // create parameter Expression\n                var argExpressions = new Expression[innerParameters.Length]; // array that will contains parameter expressions\n                for (var i = 0; i < innerParameters.Length; i++)\n                {\n                    var indexedAccess = Expression.ArrayIndex(parameterExpression, Expression.Constant(i));\n\n                    if (!innerParameters[i].ParameterType.IsClass)\n                    {\n                        // we should create local variable that will store parameter value\n                        var localVariable = Expression.Variable(innerParameters[i].ParameterType, \"localVariable\");\n\n                        var block = Expression.Block(new[] { localVariable },\n                            Expression.IfThenElse(Expression.Equal(indexedAccess, Expression.Constant(null)),\n                                Expression.Assign(localVariable, Expression.Default(innerParameters[i].ParameterType)),\n                                Expression.Assign(localVariable, Expression.Convert(indexedAccess, innerParameters[i].ParameterType))\n                            ),\n                            localVariable\n                        );\n\n                        argExpressions[i] = block;\n                    }\n                    else\n                    {\n                        argExpressions[i] = Expression.Convert(indexedAccess, innerParameters[i].ParameterType);\n                    }\n                }\n                // create expression that represents call to specified ctor with the specified arguments.\n                var newExpression = Expression.New(ctorInfo, argExpressions);\n\n                return Expression.Lambda<Func<object?[], object>>(newExpression, parameterExpression)\n                .Compile();\n            });\n            return func.Invoke(ctorParams);\n        });\n\n        return newFunc.Invoke(this);\n    }\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    public object? GetService(Type serviceType)\n    {\n        if (_disposed)\n        {\n            throw new InvalidOperationException($\"can not get scope service from a disposed scope, serviceType: {serviceType.FullName}\");\n        }\n\n        var serviceDefinition = _services.LastOrDefault(s => s.ServiceType == serviceType);\n        if (null == serviceDefinition)\n        {\n            if (serviceType.IsGenericType)\n            {\n                var genericType = serviceType.GetGenericTypeDefinition();\n                serviceDefinition = _services.LastOrDefault(s => s.ServiceType == genericType);\n                if (null == serviceDefinition)\n                {\n                    var innerServiceType = serviceType.GetGenericArguments().First();\n                    if (typeof(IEnumerable<>).MakeGenericType(innerServiceType)\n                        .IsAssignableFrom(serviceType))\n                    {\n                        var innerRegType = innerServiceType;\n                        if (innerServiceType.IsGenericType)\n                        {\n                            innerRegType = innerServiceType.GetGenericTypeDefinition();\n                        }\n                        //\n                        var list = new List<object>(4);\n                        foreach (var def in _services.Where(s => s.ServiceType == innerRegType))\n                        {\n                            object? svc;\n                            if (def.ServiceLifetime == ServiceLifetime.Singleton)\n                            {\n                                svc = _singletonInstances.GetOrAdd(new ServiceKey(innerServiceType, def), _ => GetServiceInstance(innerServiceType, def));\n                            }\n                            else if (def.ServiceLifetime == ServiceLifetime.Scoped)\n                            {\n                                svc = _scopedInstances.GetOrAdd(new ServiceKey(innerServiceType, def), _ => GetServiceInstance(innerServiceType, def));\n                            }\n                            else\n                            {\n                                svc = GetServiceInstance(innerServiceType, def);\n                                if (svc is IDisposable)\n                                {\n                                    _transientDisposables.Add(svc);\n                                }\n                            }\n                            if (svc != null)\n                            {\n                                list.Add(svc);\n                            }\n                        }\n\n                        var methodInfo = typeof(Enumerable)\n                            .GetMethod(\"Cast\", BindingFlags.Static | BindingFlags.Public);\n                        if (methodInfo != null)\n                        {\n                            var genericMethod = methodInfo.MakeGenericMethod(innerServiceType);\n                            var castValue = genericMethod.Invoke(null, new object[] { list });\n                            if (typeof(IEnumerable<>).MakeGenericType(innerServiceType) == serviceType)\n                            {\n                                return castValue;\n                            }\n                            var toArrayMethod = typeof(Enumerable).GetMethod(\"ToArray\", BindingFlags.Static | BindingFlags.Public)\n                                ?.MakeGenericMethod(innerServiceType);\n\n                            return toArrayMethod?.Invoke(null, [castValue]);\n                        }\n                        return list;\n                    }\n\n                    return null;\n                }\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        if (_isRootScope && serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)\n        {\n            throw new InvalidOperationException($\"can not get scope service from the root scope, serviceType: {serviceType.FullName}\");\n        }\n\n        if (serviceDefinition.ServiceLifetime == ServiceLifetime.Singleton)\n        {\n            return _singletonInstances.GetOrAdd(new ServiceKey(serviceType, serviceDefinition), (t) => GetServiceInstance(t.ServiceType, serviceDefinition));\n        }\n\n        if (serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)\n        {\n            return _scopedInstances.GetOrAdd(new ServiceKey(serviceType, serviceDefinition), (t) => GetServiceInstance(t.ServiceType, serviceDefinition));\n        }\n\n        var svc1 = GetServiceInstance(serviceType, serviceDefinition);\n        if (svc1 is IDisposable)\n        {\n            _transientDisposables.Add(svc1);\n        }\n        return svc1;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/ServiceContainerBuilder.cs",
    "content": "﻿using System.Collections;\n\nnamespace WeihanLi.Common.DependencyInjection;\n\npublic interface IServiceContainerBuilder : IEnumerable<ServiceDefinition>\n{\n    IServiceContainerBuilder Add(ServiceDefinition item);\n\n    IServiceContainerBuilder TryAdd(ServiceDefinition item);\n\n    IServiceContainer Build();\n}\n\npublic sealed class ServiceContainerBuilder : IServiceContainerBuilder\n{\n    private readonly List<ServiceDefinition> _services = [];\n\n    public IServiceContainerBuilder Add(ServiceDefinition item)\n    {\n        if (_services.Any(s => s.ServiceType == item.ServiceType && s.GetImplementType() == item.GetImplementType()))\n        {\n            return this;\n        }\n\n        _services.Add(item);\n        return this;\n    }\n\n    public IServiceContainerBuilder TryAdd(ServiceDefinition item)\n    {\n        if (_services.Any(s => s.ServiceType == item.ServiceType))\n        {\n            return this;\n        }\n        _services.Add(item);\n        return this;\n    }\n\n    public IServiceContainer Build() => new ServiceContainer(_services);\n\n    public IEnumerator<ServiceDefinition> GetEnumerator() => _services.GetEnumerator();\n\n    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/ServiceContainerBuilderExtensions.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.DependencyInjection;\n\npublic static partial class ServiceContainerBuilderExtensions\n{\n    public static IServiceContainerBuilder AddSingleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this IServiceContainerBuilder serviceContainerBuilder, TService service)\n    {\n        Guard.NotNull(service, nameof(service));\n        serviceContainerBuilder.Add(new ServiceDefinition(service!, typeof(TService)));\n        return serviceContainerBuilder;\n    }\n\n    /// <summary>\n    /// RegisterAssemblyTypes\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyTypes(this IServiceContainerBuilder services, params Assembly[] assemblies)\n        => RegisterAssemblyTypes(services, null, ServiceLifetime.Singleton, assemblies);\n\n    /// <summary>\n    /// RegisterAssemblyTypes\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyTypes(this IServiceContainerBuilder services,\n        ServiceLifetime serviceLifetime, params Assembly[] assemblies)\n        => RegisterAssemblyTypes(services, null, serviceLifetime, assemblies);\n\n    /// <summary>\n    /// RegisterAssemblyTypes\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyTypes(this IServiceContainerBuilder services,\n        Func<Type, bool>? typesFilter, params Assembly[] assemblies)\n        => RegisterAssemblyTypes(services, typesFilter, ServiceLifetime.Singleton, assemblies);\n\n    /// <summary>\n    /// RegisterAssemblyTypes\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyTypes(this IServiceContainerBuilder services, Func<Type, bool>? typesFilter, ServiceLifetime serviceLifetime, params Assembly[] assemblies)\n    {\n        Guard.NotNull(assemblies, nameof(assemblies));\n\n        if (assemblies.Length == 0)\n        {\n            assemblies = ReflectHelper.GetAssemblies();\n        }\n\n        var types = assemblies\n            .Select(assembly => assembly.GetTypes())\n            .SelectMany(t => t)\n            .Where(t => !t.IsAbstract)\n            ;\n        if (typesFilter != null)\n        {\n            types = types.Where(typesFilter);\n        }\n\n        foreach (var type in types)\n        {\n            services.Add(new ServiceDefinition(type, type, serviceLifetime));\n        }\n\n        return services;\n    }\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyTypesAsImplementedInterfaces(this IServiceContainerBuilder services,\n        params Assembly[] assemblies)\n        => RegisterAssemblyTypesAsImplementedInterfaces(services, typesFilter: null, ServiceLifetime.Singleton, assemblies);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyTypesAsImplementedInterfaces(this IServiceContainerBuilder services,\n        ServiceLifetime serviceLifetime, params Assembly[] assemblies)\n        => RegisterAssemblyTypesAsImplementedInterfaces(services, typesFilter: null, serviceLifetime, assemblies);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces, singleton by default\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyTypesAsImplementedInterfaces(this IServiceContainerBuilder services, Func<Type, bool>? typesFilter, params Assembly[] assemblies)\n        => RegisterAssemblyTypesAsImplementedInterfaces(services, typesFilter: typesFilter, ServiceLifetime.Singleton, assemblies);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyTypesAsImplementedInterfaces(\n        this IServiceContainerBuilder services, Func<Type, bool>? typesFilter,\n        ServiceLifetime serviceLifetime, params Assembly[] assemblies)\n        => RegisterAssemblyTypesAsImplementedInterfaces(services, typesFilter, null, serviceLifetime, assemblies);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"interfaceTypeFilter\">filter interface types to register</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyTypesAsImplementedInterfaces(this IServiceContainerBuilder services, Func<Type, bool>? typesFilter, Func<Type, bool>? interfaceTypeFilter, ServiceLifetime serviceLifetime, params Assembly[] assemblies)\n    {\n        Guard.NotNull(assemblies);\n        if (assemblies.Length == 0)\n        {\n            assemblies = ReflectHelper.GetAssemblies();\n        }\n\n        var types = assemblies\n            .Select(assembly => assembly.GetTypes())\n            .SelectMany(t => t)\n            .Where(t => !t.IsAbstract)\n            ;\n        if (typesFilter != null)\n        {\n            types = types.Where(typesFilter);\n        }\n\n        foreach (var type in types)\n        {\n            foreach (var implementedInterface in type.GetImplementedInterfaces())\n            {\n                if (interfaceTypeFilter?.Invoke(implementedInterface) != false)\n                {\n                    services.Add(new ServiceDefinition(implementedInterface, type, serviceLifetime));\n                }\n            }\n        }\n\n        return services;\n    }\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"type\">type</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <returns>services</returns>\n    public static IServiceContainerBuilder RegisterTypeAsImplementedInterfaces(\n        this IServiceContainerBuilder services, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type type,\n        ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)\n        => RegisterTypeAsImplementedInterfaces(services, type, null, serviceLifetime);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"type\">type</param>\n    /// <param name=\"interfaceTypeFilter\">interfaceTypeFilter</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <returns>services</returns>\n    public static IServiceContainerBuilder RegisterTypeAsImplementedInterfaces(this IServiceContainerBuilder services, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type type, Func<Type, bool>? interfaceTypeFilter, ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)\n    {\n        Guard.NotNull(type);\n        foreach (var interfaceType in type.GetImplementedInterfaces())\n        {\n            if (interfaceTypeFilter?.Invoke(interfaceType) != false)\n            {\n                services.Add(new ServiceDefinition(interfaceType, type, serviceLifetime));\n            }\n        }\n        return services;\n    }\n\n    /// <summary>\n    /// Register Module\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"module\">service module</param>\n    /// <returns>services</returns>\n    public static IServiceContainerBuilder RegisterModule<TServiceModule>(this IServiceContainerBuilder services, TServiceModule module)\n        where TServiceModule : IServiceContainerModule\n    {\n        Guard.NotNull(module, nameof(module));\n        module.ConfigureServices(services);\n        return services;\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceContainerBuilder RegisterAssemblyModules(\n        this IServiceContainerBuilder serviceContainerBuilder, params Assembly[] assemblies)\n    {\n        Guard.NotNull(assemblies, nameof(assemblies));\n\n        if (assemblies.Length == 0)\n        {\n            assemblies = ReflectHelper.GetAssemblies();\n        }\n\n        foreach (var type in assemblies.\n            SelectMany(ass => ass.GetExportedTypes())\n            .Where(t => t.IsClass && !t.IsAbstract && typeof(IServiceContainerModule).IsAssignableFrom(t))\n        )\n        {\n            try\n            {\n                if (Activator.CreateInstance(type) is IServiceContainerModule module)\n                {\n                    module.ConfigureServices(serviceContainerBuilder);\n                }\n            }\n            catch (Exception e)\n            {\n                Console.WriteLine(e);\n            }\n        }\n        return serviceContainerBuilder;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/ServiceContainerBuilderExtensions.generated.cs",
    "content": "﻿\nnamespace WeihanLi.Common.DependencyInjection;\n\npublic static partial class ServiceContainerBuilderExtensions\n{\n    public static IServiceContainerBuilder AddSingleton(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType)\n    {\n        serviceContainerBuilder.Add(new ServiceDefinition(serviceType, ServiceLifetime.Singleton));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddSingleton(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType, Type implementType)\n    {\n        serviceContainerBuilder.Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Singleton));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddSingleton<TService>(this IServiceContainerBuilder serviceContainerBuilder, Func<IServiceProvider, object> func)\n    {\n        serviceContainerBuilder.Add(ServiceDefinition.Singleton<TService>(func));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddSingleton<TService>(this IServiceContainerBuilder serviceContainerBuilder)\n    {\n        serviceContainerBuilder.Add(ServiceDefinition.Singleton<TService>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddSingleton<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TServiceImplement>(this IServiceContainerBuilder serviceContainerBuilder) where TServiceImplement : TService\n    {\n        serviceContainerBuilder.Add(ServiceDefinition.Singleton<TService, TServiceImplement>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddScoped(this IServiceContainerBuilder serviceContainerBuilder, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]Type serviceType)\n    {\n        serviceContainerBuilder.Add(new ServiceDefinition(serviceType, ServiceLifetime.Scoped));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddScoped(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]Type implementType)\n    {\n        serviceContainerBuilder.Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Scoped));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddScoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TService>(this IServiceContainerBuilder serviceContainerBuilder, Func<IServiceProvider, object> func)\n    {\n        serviceContainerBuilder.Add(ServiceDefinition.Scoped<TService>(func));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddScoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TService>(this IServiceContainerBuilder serviceContainerBuilder)\n    {\n        serviceContainerBuilder.Add(ServiceDefinition.Scoped<TService>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddScoped<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TServiceImplement>(this IServiceContainerBuilder serviceContainerBuilder) where TServiceImplement : TService\n    {\n        serviceContainerBuilder.Add(ServiceDefinition.Scoped<TService, TServiceImplement>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddTransient(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType)\n    {\n        serviceContainerBuilder.Add(new ServiceDefinition(serviceType, ServiceLifetime.Transient));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddTransient(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType, Type implementType)\n    {\n        serviceContainerBuilder.Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Transient));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddTransient<TService>(this IServiceContainerBuilder serviceContainerBuilder, Func<IServiceProvider, object> func)\n    {\n        serviceContainerBuilder.Add(ServiceDefinition.Transient<TService>(func));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddTransient<TService>(this IServiceContainerBuilder serviceContainerBuilder)\n    {\n        serviceContainerBuilder.Add(ServiceDefinition.Transient<TService>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder AddTransient<TService, TServiceImplement>(this IServiceContainerBuilder serviceContainerBuilder) where TServiceImplement : TService\n    {\n        serviceContainerBuilder.Add(ServiceDefinition.Transient<TService, TServiceImplement>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddSingleton(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType)\n    {\n        serviceContainerBuilder.TryAdd(new ServiceDefinition(serviceType, ServiceLifetime.Singleton));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddSingleton(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType, Type implementType)\n    {\n        serviceContainerBuilder.TryAdd(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Singleton));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddSingleton<TService>(this IServiceContainerBuilder serviceContainerBuilder, Func<IServiceProvider, object> func)\n    {\n        serviceContainerBuilder.TryAdd(ServiceDefinition.Singleton<TService>(func));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddSingleton<TService>(this IServiceContainerBuilder serviceContainerBuilder)\n    {\n        serviceContainerBuilder.TryAdd(ServiceDefinition.Singleton<TService>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddSingleton<TService, TServiceImplement>(this IServiceContainerBuilder serviceContainerBuilder) where TServiceImplement : TService\n    {\n        serviceContainerBuilder.TryAdd(ServiceDefinition.Singleton<TService, TServiceImplement>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddScoped(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType)\n    {\n        serviceContainerBuilder.TryAdd(new ServiceDefinition(serviceType, ServiceLifetime.Scoped));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddScoped(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType, Type implementType)\n    {\n        serviceContainerBuilder.TryAdd(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Scoped));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddScoped<TService>(this IServiceContainerBuilder serviceContainerBuilder, Func<IServiceProvider, object> func)\n    {\n        serviceContainerBuilder.TryAdd(ServiceDefinition.Scoped<TService>(func));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddScoped<TService>(this IServiceContainerBuilder serviceContainerBuilder)\n    {\n        serviceContainerBuilder.TryAdd(ServiceDefinition.Scoped<TService>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddScoped<TService, TServiceImplement>(this IServiceContainerBuilder serviceContainerBuilder) where TServiceImplement : TService\n    {\n        serviceContainerBuilder.TryAdd(ServiceDefinition.Scoped<TService, TServiceImplement>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddTransient(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType)\n    {\n        serviceContainerBuilder.TryAdd(new ServiceDefinition(serviceType, ServiceLifetime.Transient));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddTransient(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType, Type implementType)\n    {\n        serviceContainerBuilder.TryAdd(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Transient));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddTransient<TService>(this IServiceContainerBuilder serviceContainerBuilder, Func<IServiceProvider, object> func)\n    {\n        serviceContainerBuilder.TryAdd(ServiceDefinition.Transient<TService>(func));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddTransient<TService>(this IServiceContainerBuilder serviceContainerBuilder)\n    {\n        serviceContainerBuilder.TryAdd(ServiceDefinition.Transient<TService>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder TryAddTransient<TService, TServiceImplement>(this IServiceContainerBuilder serviceContainerBuilder) where TServiceImplement : TService\n    {\n        serviceContainerBuilder.TryAdd(ServiceDefinition.Transient<TService, TServiceImplement>());\n        return serviceContainerBuilder;\n    }\n\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/ServiceContainerBuilderExtensions.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n<#@ import namespace=\"System.Diagnostic.CodeAnalysis\" #>\n<#@ output extension=\".generated.cs\" #>\n\nnamespace WeihanLi.Common.DependencyInjection;\n\npublic static partial class ServiceContainerBuilderExtensions\n{\n#region warning disable\n#pragma warning disable IL2026\n#pragma warning disable IL2067\n#pragma warning disable IL2091\n#endregion\n\n<#\n            var lifetimes= new []{ \"Singleton\", \"Scoped\", \"Transient\" };\n            var prefixes= new [] {\"\", \"Try\"};\nforeach(var prefix in prefixes)\nforeach(var lifetime in lifetimes)\n            {\n#>\n    public static IServiceContainerBuilder <#= prefix#>Add<#=lifetime #>(this IServiceContainerBuilder serviceContainerBuilder, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]Type serviceType)\n    {\n        serviceContainerBuilder.<#= prefix#>Add(new ServiceDefinition(serviceType, ServiceLifetime.<#=lifetime #>));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder <#= prefix#>Add<#=lifetime #>(this IServiceContainerBuilder serviceContainerBuilder, Type serviceType, Type implementType)\n    {\n        serviceContainerBuilder.<#= prefix#>Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.<#=lifetime #>));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder <#= prefix#>Add<#=lifetime #><[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TService>(this IServiceContainerBuilder serviceContainerBuilder, Func<IServiceProvider, object> func)\n    {\n        serviceContainerBuilder.<#= prefix#>Add(ServiceDefinition.<#=lifetime #><TService>(func));\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder <#= prefix#>Add<#=lifetime #><[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TService>(this IServiceContainerBuilder serviceContainerBuilder)\n    {\n        serviceContainerBuilder.<#= prefix#>Add(ServiceDefinition.<#=lifetime #><TService>());\n        return serviceContainerBuilder;\n    }\n\n    public static IServiceContainerBuilder <#= prefix#>Add<#=lifetime #><TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TServiceImplement>(this IServiceContainerBuilder serviceContainerBuilder) where TServiceImplement : TService\n    {\n        serviceContainerBuilder.<#= prefix#>Add(ServiceDefinition.<#=lifetime #><TService, TServiceImplement>());\n        return serviceContainerBuilder;\n    }\n\n<#\n  }\n#>\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/ServiceContainerModule.cs",
    "content": "﻿namespace WeihanLi.Common.DependencyInjection;\n\npublic interface IServiceContainerModule\n{\n    void ConfigureServices(IServiceContainerBuilder serviceContainerBuilder);\n}\n\npublic abstract class ServiceContainerModule : IServiceContainerModule\n{\n    public abstract void ConfigureServices(IServiceContainerBuilder serviceContainerBuilder);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/ServiceDefinition.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\n\nnamespace WeihanLi.Common.DependencyInjection;\n\npublic class ServiceDefinition\n{\n    public ServiceLifetime ServiceLifetime { get; }\n\n    public Type? ImplementType { get; }\n\n    public Type ServiceType { get; }\n\n    public object? ImplementationInstance { get; }\n\n    public Func<IServiceProvider, object>? ImplementationFactory { get; }\n\n    public Type GetImplementType()\n    {\n        if (ImplementationInstance != null)\n            return ImplementationInstance.GetType();\n\n        if (ImplementationFactory != null)\n            return ImplementationFactory.Method.ReturnType;\n\n        if (ImplementType != null)\n            return ImplementType;\n\n        return ServiceType;\n    }\n\n    public ServiceDefinition(object instance, Type serviceType)\n    {\n        ImplementationInstance = instance;\n        ServiceType = serviceType;\n        ServiceLifetime = ServiceLifetime.Singleton;\n    }\n\n    public ServiceDefinition(Type serviceType, ServiceLifetime serviceLifetime) : this(serviceType, serviceType, serviceLifetime)\n    {\n    }\n\n    public ServiceDefinition(Type serviceType, Type? implementType, ServiceLifetime serviceLifetime)\n    {\n        ServiceType = serviceType;\n        ImplementType = implementType ?? serviceType;\n        ServiceLifetime = serviceLifetime;\n    }\n\n    public ServiceDefinition(Type serviceType, Func<IServiceProvider, object> factory, ServiceLifetime serviceLifetime)\n    {\n        ServiceType = serviceType;\n        ImplementationFactory = factory;\n        ServiceLifetime = serviceLifetime;\n    }\n\n    public static ServiceDefinition Singleton<TService>(Func<IServiceProvider, object> factory)\n    {\n        return new(typeof(TService), factory, ServiceLifetime.Singleton);\n    }\n\n    public static ServiceDefinition Singleton<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TServiceImplement>() where TServiceImplement : TService\n    {\n        return new(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Singleton);\n    }\n\n    public static ServiceDefinition Singleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>()\n    {\n        return new(typeof(TService), ServiceLifetime.Singleton);\n    }\n\n    public static ServiceDefinition Scoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(Func<IServiceProvider, object> factory)\n    {\n        return new(typeof(TService), factory, ServiceLifetime.Scoped);\n    }\n\n    public static ServiceDefinition Scoped<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TServiceImplement>() where TServiceImplement : TService\n    {\n        return new(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Scoped);\n    }\n\n    public static ServiceDefinition Scoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>()\n    {\n        return new(typeof(TService), ServiceLifetime.Scoped);\n    }\n\n    public static ServiceDefinition Transient<TService>(Func<IServiceProvider, object> factory)\n    {\n        return new(typeof(TService), factory, ServiceLifetime.Transient);\n    }\n\n    public static ServiceDefinition Transient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>()\n    {\n        return new(typeof(TService), ServiceLifetime.Transient);\n    }\n\n    public static ServiceDefinition Transient<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TServiceImplement>() where TServiceImplement : TService\n    {\n        return new(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Transient);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyInjection/ServiceLifetime.cs",
    "content": "﻿namespace WeihanLi.Common.DependencyInjection;\n\npublic enum ServiceLifetime\n{\n    /// <summary>\n    /// Specifies that a single instance of the service will be created.\n    /// </summary>\n    Singleton = 0,\n\n    /// <summary>\n    /// Specifies that a new instance of the service will be created for each scope.\n    /// </summary>\n    Scoped = 1,\n\n    /// <summary>\n    /// Specifies that a new instance of the service will be created every time it is requested.\n    /// </summary>\n    Transient = 2,\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/DependencyResolver.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\nusing WeihanLi.Common.DependencyInjection;\n\nnamespace WeihanLi.Common;\n\n/// <summary>\n/// DependencyResolver\n/// Service locator pattern\n/// </summary>\npublic static class DependencyResolver\n{\n    private static readonly Lock _lock = new();\n    public static IDependencyResolver Current { get; private set; } = new DefaultDependencyResolver();\n\n    public static TService? ResolveService<TService>() => Current.ResolveService<TService>();\n\n    public static TService ResolveRequiredService<TService>() => Current.ResolveRequiredService<TService>();\n\n    public static IEnumerable<TService> ResolveServices<TService>() => Current.ResolveServices<TService>();\n\n    public static bool TryInvoke<TService>(Action<TService> action) => Current.TryInvokeService(action);\n\n    public static Task<bool> TryInvokeAsync<TService>(Func<TService, Task> action) => Current.TryInvokeServiceAsync(action);\n\n    public static void SetDependencyResolver(IDependencyResolver dependencyResolver)\n    {\n        lock (_lock)\n        {\n            Current = dependencyResolver;\n        }\n    }\n\n    public static void SetDependencyResolver(IServiceContainer serviceContainer) => SetDependencyResolver(new ServiceContainerDependencyResolver(serviceContainer));\n\n    public static void SetDependencyResolver(IServiceProvider serviceProvider)\n    {\n        Guard.NotNull(serviceProvider);\n        if (serviceProvider is ServiceProvider microServiceProvider)\n            SetDependencyResolver(new ServiceProviderDependencyResolver(microServiceProvider));\n        else\n            SetDependencyResolver(serviceProvider.GetService);\n    }\n\n    public static void SetDependencyResolver(Func<Type, object?> getServiceFunc) => SetDependencyResolver(getServiceFunc, serviceType => (IEnumerable<object>)Guard.NotNull(getServiceFunc(typeof(IEnumerable<>).MakeGenericType(serviceType))));\n\n    public static void SetDependencyResolver(Func<Type, object?> getServiceFunc, Func<Type, IEnumerable<object>> getServicesFunc) => SetDependencyResolver(new DelegateBasedDependencyResolver(getServiceFunc, getServicesFunc));\n\n    public static void SetDependencyResolver(IServiceCollection services) => SetDependencyResolver(new ServiceProviderDependencyResolver(services.BuildServiceProvider()));\n\n    private sealed class ServiceProviderDependencyResolver(ServiceProvider serviceProvider) : IDependencyResolver\n    {\n        public object? GetService(Type serviceType)\n        {\n            return serviceProvider.GetService(serviceType);\n        }\n\n        [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n        [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n        public IEnumerable<object> GetServices(Type serviceType)\n        {\n            return serviceProvider.GetServices(serviceType);\n        }\n\n        public bool TryInvokeService<TService>(Action<TService> action)\n        {\n            Guard.NotNull(action, nameof(action));\n            using var scope = serviceProvider.CreateScope();\n            var service = scope.ServiceProvider.GetService<TService>();\n            if (service is null)\n                return false;\n            action.Invoke(service);\n            return true;\n        }\n\n        public async Task<bool> TryInvokeServiceAsync<TService>(Func<TService, Task> action)\n        {\n            Guard.NotNull(action, nameof(action));\n            await using var scope = serviceProvider.CreateAsyncScope();\n            var service = scope.ServiceProvider.GetService<TService>();\n            if (service is null)\n                return false;\n            await action.Invoke(service);\n            return true;\n        }\n    }\n\n    private sealed class DefaultDependencyResolver : IDependencyResolver\n    {\n        public object? GetService([DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes.PublicParameterlessConstructor))] Type serviceType)\n        {\n            // Since attempting to create an instance of an interface or an abstract type results in an exception, immediately return null\n            // to improve performance and the debugging experience with first-chance exceptions enabled.\n            if (serviceType.IsInterface || serviceType.IsAbstract)\n            {\n                return null;\n            }\n            try\n            {\n                return Activator.CreateInstance(serviceType);\n            }\n            catch\n            {\n                return null;\n            }\n        }\n\n        public IEnumerable<object> GetServices(Type serviceType) => Enumerable.Empty<object>();\n\n        public bool TryInvokeService<TService>(Action<TService>? action)\n        {\n            var service = GetService(typeof(TService));\n            if (null == service || action == null)\n            {\n                return false;\n            }\n            action.Invoke((TService)service);\n            return true;\n        }\n\n        public async Task<bool> TryInvokeServiceAsync<TService>(Func<TService, Task>? action)\n        {\n            var service = GetService(typeof(TService));\n            if (null == service || action == null)\n            {\n                return false;\n            }\n            await action.Invoke((TService)service);\n            return true;\n        }\n    }\n\n    private sealed class DelegateBasedDependencyResolver(Func<Type, object?> getService, Func<Type, IEnumerable<object>> getServices) : IDependencyResolver\n    {\n        private readonly Func<Type, object?> _getService = Guard.NotNull(getService);\n        private readonly Func<Type, IEnumerable<object>> _getServices = Guard.NotNull(getServices);\n\n        public object? GetService(Type serviceType)\n        => _getService(serviceType);\n\n        public IEnumerable<object> GetServices(Type serviceType)\n            => _getServices(serviceType);\n\n        public bool TryInvokeService<TService>(Action<TService>? action)\n        {\n            var svc = GetService(typeof(TService));\n            if (action != null && svc is TService service)\n            {\n                action.Invoke(service);\n                return true;\n            }\n            return false;\n        }\n\n        public async Task<bool> TryInvokeServiceAsync<TService>(Func<TService, Task>? action)\n        {\n            var svc = GetService(typeof(TService));\n            if (action != null && svc is TService service)\n            {\n                await action.Invoke(service);\n                return true;\n            }\n            return false;\n        }\n    }\n\n    private sealed class ServiceContainerDependencyResolver(IServiceContainer serviceContainer) : IDependencyResolver\n    {\n        public object? GetService(Type serviceType)\n        {\n            return serviceContainer.GetService(serviceType);\n        }\n\n        public IEnumerable<object> GetServices(Type serviceType)\n        {\n            return (IEnumerable<object>)Guard.NotNull(serviceContainer.GetService(typeof(IEnumerable<>).MakeGenericType(serviceType)));\n        }\n\n        public bool TryInvokeService<TService>(Action<TService> action)\n        {\n            Guard.NotNull(action, nameof(action));\n\n            using var scope = serviceContainer.CreateScope();\n            var svc = scope.GetService(typeof(TService));\n            if (svc is TService service)\n            {\n                action.Invoke(service);\n                return true;\n            }\n            return false;\n        }\n\n        public async Task<bool> TryInvokeServiceAsync<TService>(Func<TService, Task> action)\n        {\n            Guard.NotNull(action, nameof(action));\n            using var scope = serviceContainer.CreateScope();\n            var svc = scope.GetService(typeof(TService));\n            if (svc is TService service)\n            {\n                await action.Invoke(service);\n                return true;\n            }\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/AckQueue.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.Runtime.CompilerServices;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Event;\n\npublic sealed class AckQueueOptions\n{\n    public TimeSpan AckTimeout { get; set; } = TimeSpan.FromMinutes(10);\n\n    public bool AutoRequeue { get; set; }\n\n    public TimeSpan RequeuePeriod { get; set; } = TimeSpan.FromMinutes(1);\n}\n\npublic sealed class AckQueue : DisposableBase\n{\n    private readonly AckQueueOptions _options;\n    private readonly ConcurrentQueue<IEvent> _queue = new();\n    private readonly ConcurrentDictionary<string, IEvent> _unAckedMessages = new();\n    private readonly Timer? _timer;\n\n    public AckQueue() : this(new()) { }\n\n    public AckQueue(AckQueueOptions options)\n    {\n        _options = options;\n        if (options.AutoRequeue)\n        {\n            _timer = new Timer(_ => RequeueUnAckedMessages(), null, options.RequeuePeriod, options.RequeuePeriod);\n        }\n    }\n\n    public Task EnqueueAsync<TEvent>(TEvent @event, EventProperties? properties = null)\n    {\n        properties ??= new EventProperties();\n        if (string.IsNullOrEmpty(properties.EventId))\n        {\n            properties.EventId = Guid.NewGuid().ToString();\n        }\n\n        if (properties.EventAt == default)\n        {\n            properties.EventAt = DateTimeOffset.Now;\n        }\n\n        var internalEvent = new EventWrapper<TEvent>\n        {\n            Data = @event,\n            Properties = properties\n        };\n\n        _queue.Enqueue(internalEvent);\n        return Task.CompletedTask;\n    }\n\n    public Task<IEvent<TEvent>?> DequeueAsync<TEvent>()\n    {\n        if (_queue.TryDequeue(out var eventWrapper))\n        {\n            _unAckedMessages.TryAdd(eventWrapper.Properties.EventId, eventWrapper);\n            return Task.FromResult((IEvent<TEvent>?)eventWrapper);\n        }\n\n        return Task.FromResult<IEvent<TEvent>?>(null);\n    }\n\n    public Task AckMessageAsync(string eventId)\n    {\n        _unAckedMessages.TryRemove(eventId, out _);\n        return Task.CompletedTask;\n    }\n\n    public void RequeueUnAckedMessages()\n    {\n        foreach (var message in _unAckedMessages)\n        {\n            if (DateTimeOffset.Now - message.Value.Properties.EventAt > _options.AckTimeout)\n            {\n                if (_unAckedMessages.TryRemove(message.Key, out var eventWrapper)\n                    && eventWrapper != null)\n                {\n                    _queue.Enqueue(eventWrapper);\n                }\n            }\n        }\n    }\n\n    public async IAsyncEnumerable<IEvent> ReadAllAsync(\n        [EnumeratorCancellation] CancellationToken cancellationToken = default)\n    {\n        while (!cancellationToken.IsCancellationRequested)\n        {\n            while (_queue.TryDequeue(out var eventWrapper))\n            {\n                _unAckedMessages.TryAdd(eventWrapper.Properties.EventId, eventWrapper);\n                yield return eventWrapper;\n            }\n\n            await Task.Delay(200, cancellationToken);\n        }\n    }\n\n    protected override void Dispose(bool disposing)\n    {\n        _timer?.Dispose();\n        base.Dispose(disposing);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/DelegateEventHandler.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Event;\n\npublic sealed class DelegateEventHandler<TEvent> : EventHandlerBase<TEvent>\n{\n    private readonly Func<TEvent, EventProperties, Task> _func;\n\n    public DelegateEventHandler(Action<TEvent> action)\n    {\n        Guard.NotNull(action);\n        _func = (e, _) =>\n        {\n            action(e);\n            return Task.CompletedTask;\n        };\n    }\n\n    public DelegateEventHandler(Action<TEvent, EventProperties> action)\n    {\n        Guard.NotNull(action);\n        _func = (e, properties) =>\n        {\n            action(e, properties);\n            return Task.CompletedTask;\n        };\n    }\n\n    public DelegateEventHandler(Func<TEvent, Task> func)\n    {\n        Guard.NotNull(func);\n        _func = (e, _) => func(e);\n    }\n\n    public DelegateEventHandler(Func<TEvent, EventProperties, Task> func)\n    {\n        _func = Guard.NotNull(func);\n    }\n\n    public override Task Handle(TEvent @event, EventProperties properties)\n    {\n        return _func.Invoke(@event, properties);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventBase.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Newtonsoft.Json;\nusing WeihanLi.Common.Services;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventBase\n{\n    /// <summary>\n    /// Event publish time\n    /// </summary>\n    DateTimeOffset EventAt { get; }\n\n    /// <summary>\n    /// eventId\n    /// </summary>\n    string EventId { get; }\n}\n\npublic abstract class EventBase : IEventBase\n{\n    [JsonProperty]\n    public DateTimeOffset EventAt { get; private set; }\n\n    [JsonProperty]\n    public string EventId { get; private set; }\n\n    protected EventBase()\n    {\n        EventId = GuidIdGenerator.Instance.NewId();\n        EventAt = DateTimeOffset.UtcNow;\n    }\n\n    protected EventBase(string eventId)\n    {\n        EventId = eventId;\n        EventAt = DateTimeOffset.UtcNow;\n    }\n\n    // https://www.newtonsoft.com/json/help/html/JsonConstructorAttribute.htm\n    [JsonConstructor]\n#if NET\n    [System.Text.Json.Serialization.JsonConstructor]\n#endif\n    protected EventBase(string eventId, DateTimeOffset eventAt)\n    {\n        EventId = eventId;\n        EventAt = eventAt;\n    }\n}\n\npublic interface IEvent\n{\n    EventProperties Properties { get; }\n    object? Data { get; }\n}\n\npublic interface IEvent<out T>\n{\n    EventProperties Properties { get; }\n    T Data { get; }\n}\n\npublic class EventWrapper<T> : IEvent, IEvent<T>\n{\n    public required T Data { get; init; }\n    object? IEvent.Data => Data;\n    public required EventProperties Properties { get; init; }\n}\n\npublic static class EventExtensions\n{\n    private static readonly JsonSerializerSettings EventSerializerSettings = JsonSerializeExtension\n        .SerializerSettingsWith(s =>\n        {\n            s.NullValueHandling = NullValueHandling.Ignore;\n            s.TypeNameHandling = TypeNameHandling.Objects;\n        });\n\n    public static string ToEventMsg<TEvent>(this TEvent @event)\n    {\n        Guard.NotNull(@event);\n        return GetEvent(@event).ToJson(EventSerializerSettings);\n    }\n\n    public static string ToEventRawMsg<TEvent>(this TEvent @event)\n    {\n        Guard.NotNull(@event);\n        return @event.ToJson(EventSerializerSettings);\n    }\n\n    private static IEvent GetEvent<TEvent>(this TEvent @event)\n    {\n        if (@event is IEvent eventEvent)\n            return eventEvent;\n\n        if (@event is IEventBase eventBase)\n            return new EventWrapper<TEvent>()\n            {\n                Data = @event,\n                Properties = new()\n                {\n                    EventAt = eventBase.EventAt,\n                    EventId = eventBase.EventId\n                }\n            };\n\n        return new EventWrapper<TEvent>\n        {\n            Data = @event,\n            Properties = new EventProperties\n            {\n                EventAt = DateTimeOffset.Now\n            }\n        };\n    }\n\n    public static TEvent ToEvent<TEvent>(this string eventMsg)\n    {\n        Guard.NotNull(eventMsg);\n        return eventMsg.JsonToObject<TEvent>(EventSerializerSettings);\n    }\n\n    public static IEvent ToEvent(this string eventMsg) => ToEvent<IEvent>(eventMsg);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventBus.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Diagnostics;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Logging;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Event;\n\n/// <summary>\n/// EventBus in process\n/// </summary>\npublic sealed class EventBus(IEventSubscriptionManager? subscriptionManager = null) : IEventBus\n{\n    private static readonly ILogHelperLogger Logger = Helpers.LogHelper.GetLogger<EventBus>();\n\n    private readonly IEventSubscriptionManager _subscriptionManager = subscriptionManager ?? new InMemoryEventSubscriptionManager();\n\n    public async Task<bool> PublishAsync<TEvent>(TEvent @event, EventProperties? properties = null)\n    {\n        properties ??= new();\n        if (string.IsNullOrEmpty(properties.EventId))\n        {\n            properties.EventId = Guid.NewGuid().ToString();\n        }\n        if (properties.EventAt == default)\n        {\n            properties.EventAt = DateTimeOffset.Now;\n        }\n        using var activity = DiagnosticHelper.ActivitySource.StartActivity();\n        if (string.IsNullOrEmpty(properties.TraceId) && Activity.Current != null)\n        {\n            properties.TraceId = Activity.Current.TraceId.ToString();\n        }\n        var handlers = _subscriptionManager.GetEventHandlers<TEvent>();\n        if (handlers.Count > 0)\n        {\n            var handlerTasks = new Task[handlers.Count];\n            handlers.ForEach((handler, index) =>\n            {\n                handlerTasks[index] = handler.Handle(@event, properties).ContinueWith(r =>\n                {\n                    Logger.Error(r.Exception,\n                        $\"handle event [{typeof(TEvent).FullName}] error, eventHandlerType:{handler.GetType().FullName}\");\n                }, TaskContinuationOptions.OnlyOnFaulted);\n            });\n            await handlerTasks.WhenAllSafely().ConfigureAwait(false);\n\n            return true;\n        }\n        return false;\n    }\n\n    public Task<bool> SubscribeAsync(Type eventType, Type eventHandlerType) => _subscriptionManager.SubscribeAsync(eventType, eventHandlerType);\n    public Task<bool> SubscribeAsync<TEvent>(IEventHandler<TEvent> eventHandler) => _subscriptionManager.SubscribeAsync(eventHandler);\n    public Task<bool> UnSubscribeAsync(Type eventType, Type eventHandlerType) => _subscriptionManager.UnSubscribeAsync(eventType, eventHandlerType);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventBusExtensions.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.DependencyInjection.Extensions;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventBuilder\n{\n    IServiceCollection Services { get; }\n}\n\ninternal sealed class EventBuilder(IServiceCollection services) : IEventBuilder\n{\n    public IServiceCollection Services { get; } = services;\n}\n\npublic static class EventBusExtensions\n{\n    public static IEventBuilder AddEvents(this IServiceCollection services)\n    {\n        services.AddOptions();\n        services.TryAddSingleton<IEventSubscriptionManager, DependencyInjectionEventSubscriptionManager>();\n        services.TryAddSingleton<IEventHandlerFactory, DefaultEventHandlerFactory>();\n        services.TryAddSingleton<IEventBus, EventBus>();\n        services.TryAddSingleton<IEventQueue, EventQueueInMemory>();\n        services.TryAddSingleton<IEventStore, EventStoreInMemory>();\n        services.TryAddSingleton<IEventPublisher, EventQueuePublisher>();\n\n        return new EventBuilder(services);\n    }\n\n    public static IEventBuilder AddEventHandler<TEvent, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TEventHandler>(this IEventBuilder eventBuilder, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)\n      where TEvent : class\n      where TEventHandler : class, IEventHandler<TEvent>\n    {\n        eventBuilder.Services.TryAddEnumerable(new ServiceDescriptor(typeof(IEventHandler<TEvent>), typeof(TEventHandler), serviceLifetime));\n        return eventBuilder;\n    }\n\n    public static IEventBuilder AddEventHandler<TEvent>(this IEventBuilder eventBuilder, IEventHandler<TEvent> eventHandler)\n        where TEvent : class\n    {\n        eventBuilder.Services.TryAddEnumerable(new ServiceDescriptor(typeof(IEventHandler<TEvent>), eventHandler));\n        return eventBuilder;\n    }\n\n    [RequiresUnreferencedCode(\"Assembly.GetTypes() requires unreferenced code\")]\n    public static IEventBuilder RegisterEventHandlers(this IEventBuilder builder, Func<Type, bool>? filter = null, ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, params Assembly[] assemblies)\n    {\n        if (assemblies.IsNullOrEmpty())\n        {\n            assemblies = Helpers.ReflectHelper.GetAssemblies();\n        }\n\n        var handlerTypes = assemblies\n            .Select(ass => ass.GetTypes())\n            .SelectMany(t => t)\n            .Where(t => !t.IsAbstract\n                        && typeof(IEventHandler).IsAssignableFrom(t)\n                        && !(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(DelegateEventHandler<>))\n                        );\n        if (filter != null)\n        {\n            handlerTypes = handlerTypes.Where(filter);\n        }\n\n        foreach (var handlerType in handlerTypes)\n        {\n            foreach (var implementedInterface in handlerType.GetTypeInfo().ImplementedInterfaces)\n            {\n                if (implementedInterface.IsGenericType\n                    && typeof(IEventHandler<>) == implementedInterface.GetGenericTypeDefinition()\n                    )\n                {\n                    builder.Services.TryAddEnumerable(new ServiceDescriptor(implementedInterface, handlerType, serviceLifetime));\n                }\n            }\n        }\n\n        return builder;\n    }\n\n    public static ICollection<IEventHandler<TEvent>> GetEventHandlers<TEvent>(this IEventSubscriptionManager eventSubscriptionManager)\n    {\n        return eventSubscriptionManager.GetEventHandlers(typeof(TEvent))\n            .Cast<IEventHandler<TEvent>>()\n            .ToArray();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventHandler.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Newtonsoft.Json.Linq;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventHandler\n{\n    Task Handle(object eventData, EventProperties properties);\n}\n\npublic interface IEventHandler<in TEvent> : IEventHandler\n{\n    /// <summary>\n    /// Handler event\n    /// </summary>\n    /// <param name=\"event\">event</param>\n    /// <param name=\"properties\">eventProperties</param>\n    Task Handle(TEvent @event, EventProperties properties);\n}\n\npublic abstract class EventHandlerBase<TEvent> : IEventHandler<TEvent>, IEventHandler\n{\n    public abstract Task Handle(TEvent @event, EventProperties eventProperties);\n\n    public virtual Task Handle(object eventData, EventProperties properties)\n    {\n        Guard.NotNull(eventData);\n\n        switch (eventData)\n        {\n            case TEvent data:\n                return Handle(data, properties);\n\n            case JObject jObject:\n                var @event = jObject.ToObject<TEvent>();\n                if (@event != null)\n                    return Handle(@event, properties);\n                break;\n\n            case string eventDataJson:\n                return Handle(eventDataJson.JsonToObject<TEvent>(), properties);\n        }\n\n        throw new ArgumentException(@$\"Unsupported event DataType:{eventData.GetType()}\", nameof(eventData));\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventHandlerFactory.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace WeihanLi.Common.Event;\n\npublic sealed class DefaultEventHandlerFactory(IEventSubscriptionManager subscriptionManager) : IEventHandlerFactory\n{\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public ICollection<IEventHandler> GetHandlers(Type eventType)\n    {\n        var eventHandlers = subscriptionManager.GetEventHandlers(eventType);\n        return eventHandlers;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventProperties.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Event;\n\npublic sealed class EventProperties : IEventBase\n{\n    public DateTimeOffset EventAt { get; set; }\n    public string EventId { get; set; } = Guid.NewGuid().ToString();\n    public string? TraceId { get; set; }\n    public string? EventSource { get; set; }\n    public string? EventType { get; set; }\n\n    public Dictionary<string, object?> Headers { get; set; } = new();\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventQueueInMemory.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Collections.Concurrent;\nusing System.Diagnostics;\nusing System.Runtime.CompilerServices;\n\n#if NET\nusing System.Threading.Channels;\n#endif\n\nnamespace WeihanLi.Common.Event;\n\npublic sealed class EventQueueInMemory : IEventQueue\n{\n#if NET\n    private readonly ConcurrentDictionary<string, Channel<IEvent>> _eventQueues = new();\n#else\n    private readonly ConcurrentDictionary<string, ConcurrentQueue<IEvent>> _eventQueues = new();\n#endif\n    public ICollection<string> GetQueues() => _eventQueues.Keys;\n\n    public Task<ICollection<string>> GetQueuesAsync() => Task.FromResult(GetQueues());\n\n    public async Task<bool> EnqueueAsync<TEvent>(string queueName, TEvent @event, EventProperties? properties = null)\n    {\n        properties ??= new();\n        if (string.IsNullOrEmpty(properties.EventId))\n        {\n            properties.EventId = Guid.NewGuid().ToString();\n        }\n        if (properties.EventAt == default)\n        {\n            properties.EventAt = DateTimeOffset.Now;\n        }\n        if (string.IsNullOrEmpty(properties.TraceId) && Activity.Current != null)\n        {\n            properties.TraceId = Activity.Current.TraceId.ToString();\n        }\n        var internalEvent = new EventWrapper<TEvent>\n        {\n            Data = @event,\n            Properties = properties\n        };\n#if NET\n        var queue = _eventQueues.GetOrAdd(queueName, _ => Channel.CreateUnbounded<IEvent>());\n        await queue.Writer.WriteAsync(internalEvent);\n#else\n        var queue = _eventQueues.GetOrAdd(queueName, _ => new ConcurrentQueue<IEvent>());\n        queue.Enqueue(internalEvent);\n        await Task.CompletedTask;\n#endif\n        return true;\n    }\n\n    public Task<IEvent<TEvent>?> DequeueAsync<TEvent>(string queueName)\n    {\n        if (_eventQueues.TryGetValue(queueName, out var queue))\n        {\n#if NET\n            if (queue.Reader.TryRead(out var eventWrapper))\n#else\n            if (queue.TryDequeue(out var eventWrapper))\n#endif\n            {\n                return Task.FromResult((IEvent<TEvent>?)eventWrapper);\n            }\n        }\n\n        return Task.FromResult<IEvent<TEvent>?>(null);\n    }\n\n    public async IAsyncEnumerable<IEvent> ReadAllAsync(string queueName,\n        [EnumeratorCancellation] CancellationToken cancellationToken = default)\n    {\n        while (!cancellationToken.IsCancellationRequested)\n        {\n            if (_eventQueues.TryGetValue(queueName, out var queue))\n            {\n#if NET\n                await foreach (var @event in queue.Reader.ReadAllAsync(cancellationToken))\n                {\n                    yield return @event;\n                }\n#else\n                while (queue.TryDequeue(out var eventWrapper))\n                {\n                    yield return eventWrapper;\n                }\n#endif\n            }\n\n            await Task.Delay(200, cancellationToken);\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventQueuePublisher.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Options;\nusing System.Diagnostics;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Event;\n\npublic class EventQueuePublisher(IEventQueue eventQueue, IOptions<EventQueuePublisherOptions> options) : IEventPublisher\n{\n    private readonly IEventQueue _eventQueue = eventQueue;\n    private readonly EventQueuePublisherOptions _options = options.Value;\n\n    public virtual Task<bool> PublishAsync<TEvent>(TEvent @event, EventProperties? properties)\n    {\n        Guard.NotNull(@event);\n\n        properties ??= new();\n        if (string.IsNullOrEmpty(properties.EventId))\n        {\n            properties.EventId = Guid.NewGuid().ToString();\n        }\n        if (properties.EventAt == default)\n        {\n            properties.EventAt = DateTimeOffset.Now;\n        }\n        using var activity = DiagnosticHelper.ActivitySource.StartActivity();\n        if (string.IsNullOrEmpty(properties.TraceId) && Activity.Current != null)\n        {\n            properties.TraceId = Activity.Current.TraceId.ToString();\n        }\n\n        var queueName = _options.EventQueueNameResolver.Invoke(@event.GetType()) ?? \"events\";\n        return _eventQueue.EnqueueAsync(queueName, @event, properties);\n    }\n}\n\npublic sealed class EventQueuePublisherOptions\n{\n    private Func<Type, string> _eventQueueNameResolver = _ => \"events\";\n\n    public Func<Type, string> EventQueueNameResolver\n    {\n        get => _eventQueueNameResolver;\n        set => _eventQueueNameResolver = Guard.NotNull(value);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventStoreInMemory.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Collections.Concurrent;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Event;\n\npublic sealed class EventStoreInMemory : IEventStore\n{\n    private readonly ConcurrentDictionary<string, IEvent> _events = new();\n\n    public Task<int> SaveEventsAsync(ICollection<IEvent> events)\n    {\n        if (events.IsNullOrEmpty())\n            return Task.FromResult(0);\n\n        return Task.FromResult(events.Count(@event => _events.TryAdd(@event.Properties.EventId, @event)));\n    }\n\n    private int DeleteEvents(ICollection<string> eventIds)\n    {\n        return eventIds.IsNullOrEmpty() ? 0 : eventIds.Count(eventId => _events.TryRemove(eventId, out _));\n    }\n\n    public Task<int> DeleteEventsAsync(ICollection<string> eventIds) => Task.FromResult(DeleteEvents(eventIds));\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/EventSubscriptionManager.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Collections.Concurrent;\nusing System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventSubscriptionManager : IEventSubscriber\n{\n    /// <summary>\n    /// Get EventHandlers for event\n    /// </summary>\n    /// <param name=\"eventType\">event</param>\n    /// <returns>event handlers types</returns>\n    ICollection<IEventHandler> GetEventHandlers(Type eventType);\n}\n\npublic sealed class InMemoryEventSubscriptionManager(IServiceProvider? serviceProvider = null)\n    : IEventSubscriptionManager\n{\n    private readonly IServiceProvider _serviceProvider = serviceProvider ?? DependencyResolver.Current;\n    private readonly ConcurrentDictionary<Type, ConcurrentSet<IEventHandler>> _eventHandlers = new();\n\n    [RequiresUnreferencedCode(\"Calls WeihanLi.Common.Helpers.ActivatorHelper.GetServiceOrCreateInstance(Type)\")]\n    private bool Subscribe(Type eventType, Type eventHandlerType)\n    {\n        var handlers = _eventHandlers.GetOrAdd(eventType, []);\n        return handlers.TryAdd((IEventHandler)Guard.NotNull(_serviceProvider.GetServiceOrCreateInstance(eventHandlerType)));\n    }\n\n    public Task<bool> SubscribeAsync(Type eventType, Type eventHandlerType)\n    {\n        return Task.FromResult(Subscribe(eventType, eventHandlerType));\n    }\n\n    public Task<bool> SubscribeAsync<TEvent>(IEventHandler<TEvent> eventHandler)\n    {\n        var handlers = _eventHandlers.GetOrAdd(typeof(TEvent), []);\n        return Task.FromResult<bool>(handlers.TryAdd(eventHandler));\n    }\n\n    public bool UnSubscribe(Type eventType, Type eventHandlerType)\n    {\n        if (_eventHandlers.TryGetValue(eventType, out var handlers))\n        {\n            var handler = handlers.FirstOrDefault(h => h.GetType() == eventHandlerType);\n            return handler is not null && handlers.TryRemove(handler);\n        }\n\n        return false;\n    }\n\n    public Task<bool> UnSubscribeAsync(Type eventType, Type eventHandlerType)\n    {\n        return Task.FromResult(UnSubscribe(eventType, eventHandlerType));\n    }\n\n    public ICollection<IEventHandler> GetEventHandlers(Type eventType)\n    {\n        return _eventHandlers[eventType];\n    }\n}\n\npublic sealed class DependencyInjectionEventSubscriptionManager(IServiceProvider serviceProvider)\n    : IEventSubscriptionManager\n{\n    public Task<bool> SubscribeAsync(Type eventType, Type eventHandlerType) => throw new NotSupportedException();\n    public Task<bool> SubscribeAsync<TEvent>(IEventHandler<TEvent> eventHandler) => throw new NotSupportedException();\n    public Task<bool> UnSubscribeAsync(Type eventType, Type eventHandlerType) => throw new NotSupportedException();\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    [RequiresDynamicCode(\"Requires dynamic code\")]\n    public ICollection<IEventHandler> GetEventHandlers(Type eventType)\n    {\n        var eventHandlerType = typeof(IEventHandler<>).MakeGenericType(eventType);\n        return serviceProvider.GetServices(eventHandlerType).Cast<IEventHandler>().ToArray();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/IEventBus.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventBus : IEventPublisher, IEventSubscriber;\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/IEventHandlerFactory.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventHandlerFactory\n{\n    ICollection<IEventHandler> GetHandlers(Type eventType);\n}\n\npublic static class EventHandlerFactoryExtensions\n{\n    public static ICollection<IEventHandler<TEvent>> GetHandlers<TEvent>(this IEventHandlerFactory eventHandlerFactory)\n    {\n        return eventHandlerFactory.GetHandlers(typeof(TEvent))\n            .Cast<IEventHandler<TEvent>>()\n            .ToArray();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/IEventPublisher.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventPublisher\n{\n    /// <summary>\n    /// publish an event async\n    /// </summary>\n    /// <typeparam name=\"TEvent\">event type</typeparam>\n    /// <param name=\"event\">event data</param>\n    /// <param name=\"properties\">properties</param>\n    /// <returns>whether the operation succeed</returns>\n    Task<bool> PublishAsync<TEvent>(TEvent @event, EventProperties? properties = null);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/IEventQueue.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Runtime.CompilerServices;\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventQueue\n{\n    Task<ICollection<string>> GetQueuesAsync();\n    Task<bool> EnqueueAsync<TEvent>(string queueName, TEvent @event, EventProperties? properties = null);\n    Task<IEvent<TEvent>?> DequeueAsync<TEvent>(string queueName);\n    IAsyncEnumerable<IEvent> ReadAllAsync(string queueName, CancellationToken cancellationToken = default);\n}\n\npublic static class EventQueueExtensions\n{\n    private const string DefaultQueueName = \"events\";\n\n    public static Task<bool> EnqueueAsync<TEvent>(this IEventQueue eventQueue, TEvent @event, EventProperties? properties = null)\n        where TEvent : class\n    {\n        return eventQueue.EnqueueAsync(DefaultQueueName, @event, properties);\n    }\n\n    public static async IAsyncEnumerable<IEvent<TEvent>> ReadEventsAsync<TEvent>(\n        this IEventQueue eventQueue,\n        string queueName,\n        [EnumeratorCancellation] CancellationToken cancellationToken = default\n        )\n    {\n        await foreach (var @event in eventQueue.ReadAllAsync(queueName, cancellationToken))\n        {\n            if (@event is IEvent<TEvent> eventEvent)\n                yield return eventEvent;\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/IEventStore.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventStore\n{\n    Task<int> SaveEventsAsync(ICollection<IEvent> events);\n    Task<int> DeleteEventsAsync(ICollection<string> eventIds);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Event/IEventSubscriber.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Event;\n\npublic interface IEventSubscriber\n{\n    /// <summary>\n    /// add event handler for event\n    /// </summary>\n    /// <param name=\"eventType\">event type</param>\n    /// <param name=\"eventHandlerType\">eventHandler type</param>\n    /// <returns>whether the operation success</returns>\n    Task<bool> SubscribeAsync(Type eventType, Type eventHandlerType);\n\n    /// <summary>\n    /// add event handler instance for event\n    /// </summary>\n    /// <param name=\"eventHandler\">event handler</param>\n    /// <typeparam name=\"TEvent\">event type</typeparam>\n    /// <returns></returns>\n    Task<bool> SubscribeAsync<TEvent>(IEventHandler<TEvent> eventHandler);\n\n    /// <summary>\n    /// remove event handler for event\n    /// </summary>\n    /// <param name=\"eventType\">event type</param>\n    /// <param name=\"eventHandlerType\">eventHandler type</param>\n    /// <returns>whether the operation success</returns>\n    Task<bool> UnSubscribeAsync(Type eventType, Type eventHandlerType);\n}\n\npublic static class EventSubscriberExtensions\n{\n    /// <summary>\n    /// add event handler for event\n    /// </summary>\n    /// <typeparam name=\"TEvent\">TEvent</typeparam>\n    /// <typeparam name=\"TEventHandler\">TEventHandler</typeparam>\n    /// <returns>whether the operation success</returns>\n    public static Task<bool> SubscribeAsync<TEvent, TEventHandler>(this IEventSubscriber subscriber)\n        where TEventHandler : class, IEventHandler<TEvent>\n    {\n        return subscriber.SubscribeAsync(typeof(TEvent), typeof(TEventHandler));\n    }\n\n    /// <summary>\n    /// remove event handler for event\n    /// </summary>\n    /// <typeparam name=\"TEvent\">TEvent</typeparam>\n    /// <typeparam name=\"TEventHandler\">TEventHandler</typeparam>\n    /// <returns>whether the operation success</returns>\n    public static Task<bool> UnSubscribeAsync<TEvent, TEventHandler>(this IEventSubscriber subscriber)\n        where TEventHandler : class, IEventHandler<TEvent>\n    {\n        return subscriber.UnSubscribeAsync(typeof(TEvent), typeof(TEventHandler));\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/CollectionExtension.cs",
    "content": "﻿using System.Collections.Specialized;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\n/// <summary>\n/// CollectionExtension\n/// </summary>\npublic static class CollectionExtension\n{\n    /// <summary>\n    ///     A NameValueCollection extension method that converts the @this to a dictionary.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as an IDictionary&lt;string,object&gt;</returns>\n    public static IDictionary<string, string?> ToDictionary(this NameValueCollection? @this)\n    {\n        var dict = new Dictionary<string, string?>();\n\n        if (@this != null)\n        {\n            foreach (var key in @this.AllKeys)\n            {\n                dict.Add(Guard.NotNull(key), @this[key]);\n            }\n        }\n\n        return dict;\n    }\n\n    /// <summary>将名值集合转换成字符串，key1=value1&amp;key2=value2，k/v会编码</summary>\n    /// <param name=\"source\">数据源</param>\n    /// <returns>字符串</returns>\n\n    public static string ToQueryString(this NameValueCollection? source)\n    {\n        if (source == null || source.Count <= 0)\n        {\n            return string.Empty;\n        }\n\n        var sb = new StringBuilder();\n\n        foreach (var key in source.AllKeys)\n        {\n            if (string.IsNullOrWhiteSpace(key))\n            {\n                continue;\n            }\n            sb.Append('&');\n            sb.Append(key.UrlEncode());\n            sb.Append('=');\n            var val = source.Get(key);\n            if (val != null)\n            {\n                sb.Append(val.UrlEncode());\n            }\n        }\n\n        return sb.Length > 0 ? sb.ToString(1, sb.Length - 1) : \"\";\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that adds only if the value satisfies the predicate.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"predicate\">The predicate.</param>\n    /// <param name=\"value\">The value.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool AddIf<T>(this ICollection<T> @this, Func<T, bool> predicate, T value)\n    {\n        if (@this.IsReadOnly) return false;\n\n        if (predicate(value))\n        {\n            @this.Add(value);\n            return true;\n        }\n\n        return false;\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that add value if the ICollection doesn't contains it already.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"value\">The value.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool AddIfNotContains<T>(this ICollection<T> @this, T value)\n    {\n        if (@this.IsReadOnly) return false;\n\n        if (!@this.Contains(value))\n        {\n            @this.Add(value);\n            return true;\n        }\n\n        return false;\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that adds a range to 'values'.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"values\">A variable-length parameters list containing values.</param>\n    public static void AddRange<T>(this ICollection<T> @this, params T[] values)\n    {\n        if (@this.IsReadOnly)\n        {\n            return;\n        }\n        foreach (var value in values)\n        {\n            @this.Add(value);\n        }\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that adds a collection of objects to the end of this collection only\n    ///     for value who satisfies the predicate.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"predicate\">The predicate.</param>\n    /// <param name=\"values\">A variable-length parameters list containing values.</param>\n    public static void AddRangeIf<T>(this ICollection<T> @this, Func<T, bool> predicate, params T[] values)\n    {\n        if (@this.IsReadOnly) return;\n        foreach (var value in values)\n        {\n            if (predicate(value))\n            {\n                @this.Add(value);\n            }\n        }\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that adds a range of values that's not already in the ICollection.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"values\">A variable-length parameters list containing values.</param>\n    public static void AddRangeIfNotContains<T>(this ICollection<T> @this, params T[] values)\n    {\n        if (@this.IsReadOnly) return;\n        foreach (var value in values)\n        {\n            if (!@this.Contains(value))\n            {\n                @this.Add(value);\n            }\n        }\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that query if '@this' contains all values.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"values\">A variable-length parameters list containing values.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool ContainsAll<T>(this ICollection<T> @this, params T[] values)\n    {\n        return values.All(@this.Contains);\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that query if '@this' contains any value.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"values\">A variable-length parameters list containing values.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool ContainsAny<T>(this ICollection<T> @this, params T[] values)\n    {\n        return values.Any(@this.Contains);\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that queries if the collection is null or is empty.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if null or empty&lt; t&gt;, false if not.</returns>\n    public static bool IsNullOrEmpty<T>([NotNullWhen(false)] this ICollection<T>? @this)\n    {\n        return @this == null || @this.Count == 0;\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that queries if the collection is not (null or is empty).\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if the collection is not (null or empty), false if not.</returns>\n    public static bool HasValue<T>([NotNullWhen(true)] this ICollection<T>? @this)\n    {\n        return @this is { Count: > 0 };\n    }\n\n    /// <summary>\n    ///     An ICollection&lt;T&gt; extension method that removes value that satisfy the predicate.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"predicate\">The predicate.</param>\n    public static void RemoveWhere<T>(this IList<T> @this, Func<T, bool> predicate)\n    {\n        if (@this.IsReadOnly || @this.Count == 0) return;\n\n        for (var i = @this.Count - 1; i >= 0; i--)\n        {\n            if (predicate(@this[i]))\n            {\n                @this.RemoveAt(i);\n            }\n        }\n    }\n\n    public static IEnumerable<T> GetRandomList<T>(this IList<T> list)\n    {\n        return Enumerable.Range(0, list.Count)\n            .OrderBy(_ => SecurityHelper.Random.Next(list.Count))\n            .Select(i => list[i])\n            ;\n    }\n\n#if NET\n    // https://github.com/more-itertools/more-itertools/blob/master/more_itertools/more.py#L3149\n    //def set_partitions_helper(L, k):\n    //n = len(L)\n    //if k == 1:\n    //    yield [L]\n    //elif n == k:\n    //    yield [[s] for s in L]\n    //else:\n    //    e, *M = L\n    //    for p in set_partitions_helper(M, k - 1):\n    //        yield [[e], *p]\n    //    for p in set_partitions_helper(M, k):\n    //        for i in range(len(p)):\n    //            yield p[:i] + [[e] + p[i]] + p[i + 1 :]\n    public static IEnumerable<T[][]> Partitions<T>(this T[] array, int batch)\n    {\n        if (batch <= 0 || array.Length < batch)\n        {\n            throw new ArgumentException(\"Invalid batch size\", nameof(batch));\n        }\n        if (batch == 1)\n        {\n            yield return new[] { array };\n        }\n        else if (batch == array.Length)\n        {\n            yield return array.Select(x => new[] { x }).ToArray();\n        }\n        else\n        {\n            var e = array[0];\n            var m = array[1..];\n            foreach (var p in Partitions(m, batch - 1))\n            {\n                yield return new[]\n                {\n                    new []{e}\n                }.Concat(p).ToArray();\n            }\n            foreach (var p in Partitions(m, batch))\n            {\n                for (var i = 0; i < p.Length; i++)\n                {\n                    yield return p[..i]\n                        .Concat(new[] { new[] { e }.Concat(p[i]).ToArray() })\n                        .Concat(p[(i + 1)..])\n                        .ToArray();\n                }\n            }\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/CompressionExtension.cs",
    "content": "﻿using System.IO.Compression;\nusing System.Text;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\n/// <summary>\n/// CompressionExtension\n/// </summary>\npublic static class CompressionExtension\n{\n    /// <summary>\n    ///     A string extension method that compress the given string to GZip byte array.\n    /// </summary>\n    /// <param name=\"this\">The stringToCompress to act on.</param>\n    /// <returns>The string compressed into a GZip byte array.</returns>\n    public static byte[] CompressGZip(this string @this)\n        => @this.CompressGZip(Encoding.UTF8);\n\n    /// <summary>\n    ///     A string extension method that compress the given string to GZip byte array.\n    /// </summary>\n    /// <param name=\"this\">The stringToCompress to act on.</param>\n    /// <param name=\"encoding\">The encoding.</param>\n    /// <returns>The string compressed into a GZip byte array.</returns>\n    public static byte[] CompressGZip(this string @this, Encoding encoding) => encoding.GetBytes(@this).CompressGZip();\n\n    /// <summary>\n    ///     A byteArray extension method that compress the given byte array to GZip byte array.\n    /// </summary>\n    /// <param name=\"bytes\">The stringToCompress to act on.</param>\n    /// <returns>The string compressed into a GZip byte array.</returns>\n    public static byte[] CompressGZip(this byte[] bytes)\n    {\n        using var memoryStream = new MemoryStream();\n        using (var zipStream = new GZipStream(memoryStream, CompressionMode.Compress))\n        {\n            zipStream.Write(bytes);\n        }\n        return memoryStream.ToArray();\n    }\n\n    public static async Task<byte[]> CompressGZipAsync(this byte[] bytes)\n    {\n        using var memoryStream = new MemoryStream();\n        using (var zipStream = new GZipStream(memoryStream, CompressionMode.Compress))\n        {\n            await zipStream.WriteAsync(bytes);\n        }\n        return memoryStream.ToArray();\n    }\n\n    public static byte[] CompressGZip(this Stream stream)\n    {\n        using var memoryStream = new MemoryStream();\n        using (var zipStream = new GZipStream(memoryStream, CompressionMode.Compress))\n        {\n            stream.CopyTo(zipStream);\n        }\n        return memoryStream.ToArray();\n    }\n\n    public static async Task<byte[]> CompressGZipAsync(this Stream stream)\n    {\n        using var memoryStream = new MemoryStream();\n        using (var zipStream = new GZipStream(memoryStream, CompressionMode.Compress))\n        {\n            await stream.CopyToAsync(zipStream);\n        }\n        return memoryStream.ToArray();\n    }\n\n    /// <summary>\n    ///     A byte[] extension method that decompress the byte array gzip to string.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The byte array gzip to string.</returns>\n    public static byte[] DecompressGZip(this byte[] @this)\n    {\n        using var memoryStream = new MemoryStream(@this);\n        return memoryStream.DecompressGZip();\n    }\n\n    public static async Task<byte[]> DecompressGZipAsync(this byte[] @this)\n    {\n        using var memoryStream = new MemoryStream(@this);\n        return await memoryStream.DecompressGZipAsync();\n    }\n\n    public static byte[] DecompressGZip(this Stream stream)\n    {\n        using var outStream = new MemoryStream();\n        using (var zipStream = new GZipStream(stream, CompressionMode.Decompress))\n        {\n            zipStream.CopyTo(outStream);\n        }\n        return outStream.ToArray();\n    }\n\n    public static async Task<byte[]> DecompressGZipAsync(this Stream stream)\n    {\n        using var outStream = new MemoryStream();\n        using (var zipStream = new GZipStream(stream, CompressionMode.Decompress))\n        {\n            await zipStream.CopyToAsync(outStream);\n        }\n        return outStream.ToArray();\n    }\n\n    public static string CompressGZipString(this byte[] bytes) => bytes.CompressGZipString(Encoding.UTF8);\n\n    public static string CompressGZipString(this byte[] bytes, Encoding encoding)\n    {\n        using var memoryStream = new MemoryStream();\n        using (var zipStream = new GZipStream(memoryStream, CompressionMode.Compress))\n        {\n            zipStream.Write(bytes);\n        }\n        return encoding.GetString(memoryStream.ToArray());\n    }\n\n    public static string DecompressGZipString(this byte[] bytes) =>\n        bytes.DecompressGZipString(Encoding.UTF8);\n\n    public static string DecompressGZipString(this byte[] bytes, Encoding encoding) => encoding.GetString(bytes.DecompressGZip());\n\n    /// <summary>\n    ///     A FileInfo extension method that creates a zip file.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    public static void CreateGZip(this FileInfo @this)\n    {\n        using var originalFileStream = @this.OpenRead();\n        using var compressedFileStream = File.Create(@this.FullName + \".gz\");\n        using var compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress);\n        originalFileStream.CopyTo(compressionStream);\n    }\n\n    /// <summary>\n    ///     A FileInfo extension method that creates a zip file.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destination\">Destination for the zip.</param>\n    public static void CreateGZip(this FileInfo @this, string destination)\n    {\n        using var originalFileStream = @this.OpenRead();\n        using var compressedFileStream = File.Create(destination);\n        using var compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress);\n        originalFileStream.CopyTo(compressionStream);\n    }\n\n    /// <summary>\n    ///     A FileInfo extension method that creates a zip file.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destination\">Destination for the zip.</param>\n    public static void CreateGZip(this FileInfo @this, FileInfo destination)\n    {\n        using var originalFileStream = @this.OpenRead();\n        using var compressedFileStream = File.Create(destination.FullName);\n        using var compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress);\n        originalFileStream.CopyTo(compressionStream);\n    }\n\n    /// <summary>\n    ///     A FileInfo extension method that extracts the g zip to directory described by\n    ///     @this.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    public static void ExtractGZipToDirectory(this FileInfo @this)\n    {\n        using var originalFileStream = @this.OpenRead();\n        var newFileName = Path.GetFileNameWithoutExtension(@this.FullName);\n\n        using var decompressedFileStream = File.Create(newFileName);\n        using var decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress);\n        decompressionStream.CopyTo(decompressedFileStream);\n    }\n\n    /// <summary>\n    ///     A FileInfo extension method that extracts the g zip to directory described by\n    ///     @this.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destination\">Destination for the.</param>\n    public static void ExtractGZipToDirectory(this FileInfo @this, string destination)\n    {\n        using var originalFileStream = @this.OpenRead();\n        using var compressedFileStream = File.Create(destination);\n        using var compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress);\n        originalFileStream.CopyTo(compressionStream);\n    }\n\n    /// <summary>\n    ///     A FileInfo extension method that extracts the g zip to directory described by\n    ///     @this.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destination\">Destination for the.</param>\n    public static void ExtractGZipToDirectory(this FileInfo @this, FileInfo destination)\n    {\n        using var originalFileStream = @this.OpenRead();\n        using var compressedFileStream = File.Create(destination.FullName);\n        using var compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress);\n        originalFileStream.CopyTo(compressionStream);\n    }\n\n    /// <summary>Opens a zip archive at the specified path and in the specified mode.</summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"mode\">\n    ///     One of the enumeration values that specifies the actions that are allowed\n    ///     on the entries in the opened archive.\n    /// </param>\n    /// <returns>A ZipArchive.</returns>\n    public static ZipArchive OpenZipFile(this FileInfo @this, ZipArchiveMode mode)\n    {\n        return ZipFile.Open(@this.FullName, mode);\n    }\n\n    /// <summary>Opens a zip archive at the specified path and in the specified mode.</summary>\n    /// <param name=\"this\">\n    ///     The path to the archive to open, specified as a relative or absolute\n    ///     path. A relative path is interpreted as relative to the current working directory.\n    /// </param>\n    /// <param name=\"mode\">\n    ///     One of the enumeration values that specifies the actions that are allowed\n    ///     on the entries in the opened archive.\n    /// </param>\n    /// <param name=\"entryNameEncoding\">\n    ///     The encoding to use when reading or writing entry names in\n    ///     this archive. Specify a value for this parameter only when an encoding is required for\n    ///     interoperability with zip archive tools and libraries that do not support UTF-8 encoding for\n    ///     entry names.\n    /// </param>\n    /// <returns>A ZipArchive.</returns>\n    public static ZipArchive OpenZipFile(this FileInfo @this, ZipArchiveMode mode, Encoding entryNameEncoding)\n    {\n        return ZipFile.Open(@this.FullName, mode, entryNameEncoding);\n    }\n\n    /// <summary>\n    ///     The path to the archive to open, specified as a relative or absolute path. A relative path is interpreted as\n    ///     relative to the current working directory.\n    /// </summary>\n    /// <param name=\"this\">\n    ///     The path to the archive to open, specified as a relative or absolute path. A relative path is\n    ///     interpreted as relative to the current working directory.\n    /// </param>\n    /// <returns>The opened zip archive.</returns>\n    public static ZipArchive OpenReadZipFile(this FileInfo @this)\n    {\n        return ZipFile.OpenRead(@this.FullName);\n    }\n\n    /// <summary>\n    ///     Extracts all the files in the specified zip archive to a directory on the file system\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destinationDirectoryName\">\n    ///     The path to the directory in which to place the\n    ///     extracted files, specified as a relative or absolute path. A relative path is interpreted as\n    ///     relative to the current working directory.\n    /// </param>\n    public static void ExtractZipFileToDirectory(this FileInfo @this, string destinationDirectoryName)\n    {\n        ZipFile.ExtractToDirectory(@this.FullName, destinationDirectoryName);\n    }\n\n    /// <summary>\n    ///     Extracts all the files in the specified zip archive to a directory on the file system and uses the specified\n    ///     character encoding for entry names.\n    /// </summary>\n    /// <param name=\"this\">The path to the archive that is to be extracted.</param>\n    /// <param name=\"destinationDirectoryName\">\n    ///     The path to the directory in which to place the extracted files, specified as a\n    ///     relative or absolute path. A relative path is interpreted as relative to the current working directory.\n    /// </param>\n    /// <param name=\"entryNameEncoding\">\n    ///     The encoding to use when reading or writing entry names in this archive. Specify a\n    ///     value for this parameter only when an encoding is required for interoperability with zip archive tools and\n    ///     libraries that do not support UTF-8 encoding for entry names.\n    /// </param>\n    public static void ExtractZipFileToDirectory(this FileInfo @this, string destinationDirectoryName, Encoding entryNameEncoding)\n    {\n        ZipFile.ExtractToDirectory(@this.FullName, destinationDirectoryName, entryNameEncoding);\n    }\n\n    /// <summary>Extracts all the files in the specified zip archive to a directory on the file system.</summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destinationDirectory\">Pathname of the destination directory.</param>\n    public static void ExtractZipFileToDirectory(this FileInfo @this, DirectoryInfo destinationDirectory)\n    {\n        ZipFile.ExtractToDirectory(@this.FullName, destinationDirectory.FullName);\n    }\n\n    /// <summary>\n    ///     Extracts all the files in the specified zip archive to a directory on the file system\n    ///     and uses the specified character encoding for entry names.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destinationDirectory\">Pathname of the destination directory.</param>\n    /// <param name=\"entryNameEncoding\">\n    ///     The encoding to use when reading or writing entry names in\n    ///     this archive. Specify a value for this parameter only when an encoding is required for\n    ///     interoperability with zip archive tools and libraries that do not support UTF-8 encoding for\n    ///     entry names.\n    /// </param>\n    public static void ExtractZipFileToDirectory(this FileInfo @this, DirectoryInfo destinationDirectory, Encoding entryNameEncoding)\n    {\n        ZipFile.ExtractToDirectory(@this.FullName, destinationDirectory.FullName, entryNameEncoding);\n    }\n\n    /// <summary>\n    ///     Creates a zip archive that contains the files and directories from the specified\n    ///     directory.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destinationArchiveFileName\">\n    ///     The path of the archive to be created, specified as a\n    ///     relative or absolute path. A relative path is interpreted as relative to the current working\n    ///     directory.\n    /// </param>\n    public static void CreateZipFile(this DirectoryInfo @this, string destinationArchiveFileName)\n    {\n        ZipFile.CreateFromDirectory(@this.FullName, destinationArchiveFileName);\n    }\n\n    /// <summary>\n    ///     Creates a zip archive that contains the files and directories from the specified\n    ///     directory, uses the specified compression level, and optionally includes the base directory.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destinationArchiveFileName\">\n    ///     The path of the archive to be created, specified as a\n    ///     relative or absolute path. A relative path is interpreted as relative to the current working\n    ///     directory.\n    /// </param>\n    /// <param name=\"compressionLevel\">\n    ///     One of the enumeration values that indicates whether to\n    ///     emphasize speed or compression effectiveness when creating the entry.\n    /// </param>\n    /// <param name=\"includeBaseDirectory\">\n    ///     true to include the directory name from\n    ///     sourceDirectoryName at the root of the archive; false to include only the contents of the\n    ///     directory.\n    /// </param>\n    public static void CreateZipFile(this DirectoryInfo @this, string destinationArchiveFileName, CompressionLevel compressionLevel, bool includeBaseDirectory)\n    {\n        ZipFile.CreateFromDirectory(@this.FullName, destinationArchiveFileName, compressionLevel, includeBaseDirectory);\n    }\n\n    /// <summary>\n    ///     Creates a zip archive that contains the files and directories from the specified directory, uses the specified\n    ///     compression level and character encoding for entry names, and optionally includes the base directory.\n    /// </summary>\n    /// <param name=\"this\">\n    ///     The path to the directory to be archived, specified as a relative or absolute path. A relative path\n    ///     is interpreted as relative to the current working directory.\n    /// </param>\n    /// <param name=\"destinationArchiveFileName\">\n    ///     The path of the archive to be created, specified as a relative or absolute\n    ///     path. A relative path is interpreted as relative to the current working directory.\n    /// </param>\n    /// <param name=\"compressionLevel\">\n    ///     One of the enumeration values that indicates whether to emphasize speed or compression\n    ///     effectiveness when creating the entry.\n    /// </param>\n    /// <param name=\"includeBaseDirectory\">\n    ///     true to include the directory name from sourceDirectoryName at the root of the\n    ///     archive; false to include only the contents of the directory.\n    /// </param>\n    /// <param name=\"entryNameEncoding\">\n    ///     The encoding to use when reading or writing entry names in this archive. Specify a\n    ///     value for this parameter only when an encoding is required for interoperability with zip archive tools and\n    ///     libraries that do not support UTF-8 encoding for entry names.\n    /// </param>\n    public static void CreateZipFile(this DirectoryInfo @this, string destinationArchiveFileName, CompressionLevel compressionLevel, bool includeBaseDirectory, Encoding entryNameEncoding)\n    {\n        ZipFile.CreateFromDirectory(@this.FullName, destinationArchiveFileName, compressionLevel, includeBaseDirectory, entryNameEncoding);\n    }\n\n    /// <summary>\n    ///     Creates a zip archive that contains the files and directories from the specified\n    ///     directory.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destinationArchiveFile\">\n    ///     The path of the archive to be created, specified as a\n    ///     relative or absolute path. A relative path is interpreted as relative to the current working\n    ///     directory.\n    /// </param>\n    public static void CreateZipFile(this DirectoryInfo @this, FileInfo destinationArchiveFile)\n    {\n        ZipFile.CreateFromDirectory(@this.FullName, destinationArchiveFile.FullName);\n    }\n\n    /// <summary>\n    ///     Creates a zip archive that contains the files and directories from the specified\n    ///     directory, uses the specified compression level, and optionally includes the base directory.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"destinationArchiveFile\">\n    ///     The path of the archive to be created, specified as a\n    ///     relative or absolute path. A relative path is interpreted as relative to the current working\n    ///     directory.\n    /// </param>\n    /// <param name=\"compressionLevel\">\n    ///     One of the enumeration values that indicates whether to\n    ///     emphasize speed or compression effectiveness when creating the entry.\n    /// </param>\n    /// <param name=\"includeBaseDirectory\">\n    ///     true to include the directory name from\n    ///     sourceDirectoryName at the root of the archive; false to include only the contents of the\n    ///     directory.\n    /// </param>\n    public static void CreateZipFile(this DirectoryInfo @this, FileInfo destinationArchiveFile, CompressionLevel compressionLevel, bool includeBaseDirectory)\n    {\n        ZipFile.CreateFromDirectory(@this.FullName, destinationArchiveFile.FullName, compressionLevel, includeBaseDirectory);\n    }\n\n    /// <summary>\n    ///     Creates a zip archive that contains the files and directories from the specified\n    ///     directory, uses the specified compression level and character encoding for entry names, and\n    ///     optionally includes the base directory.\n    /// </summary>\n    /// <param name=\"this\">\n    ///     The path to the directory to be archived, specified as a relative or\n    ///     absolute path. A relative path is interpreted as relative to the current working directory.\n    /// </param>\n    /// <param name=\"destinationArchiveFile\">\n    ///     The path of the archive to be created, specified as a\n    ///     relative or absolute path. A relative path is interpreted as relative to the current working\n    ///     directory.\n    /// </param>\n    /// <param name=\"compressionLevel\">\n    ///     One of the enumeration values that indicates whether to\n    ///     emphasize speed or compression effectiveness when creating the entry.\n    /// </param>\n    /// <param name=\"includeBaseDirectory\">\n    ///     true to include the directory name from\n    ///     sourceDirectoryName at the root of the archive; false to include only the contents of the\n    ///     directory.\n    /// </param>\n    /// <param name=\"entryNameEncoding\">\n    ///     The encoding to use when reading or writing entry names in\n    ///     this archive. Specify a value for this parameter only when an encoding is required for\n    ///     interoperability with zip archive tools and libraries that do not support UTF-8 encoding for\n    ///     entry names.\n    /// </param>\n    public static void CreateZipFile(this DirectoryInfo @this, FileInfo destinationArchiveFile, CompressionLevel compressionLevel, bool includeBaseDirectory, Encoding entryNameEncoding)\n    {\n        ZipFile.CreateFromDirectory(@this.FullName, destinationArchiveFile.FullName, compressionLevel, includeBaseDirectory, entryNameEncoding);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/ConfigurationExtension.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Text.RegularExpressions;\nusing WeihanLi.Common;\nusing WeihanLi.Extensions;\n\n// ReSharper disable once CheckNamespace\nnamespace Microsoft.Extensions.Configuration;\n\n/// <summary>\n/// Provides extensions for <see cref=\"IConfiguration\"/> instances.\n/// </summary>\npublic static class ConfigurationExtension\n{\n    #region Placeholder\n\n    /// <summary>\n    /// A regex which matches tokens in the following format: $(Item:Sub1:Sub2).\n    /// inspired by https://github.com/henkmollema/ConfigurationPlaceholders\n    /// </summary>\n    private static readonly Regex ConfigPlaceholderRegex = new(@\"\\$\\(([A-Za-z0-9:_]+?)\\)\");\n\n    /// <summary>\n    /// Replaces the placeholders in the specified <see cref=\"IConfiguration\"/> instance.\n    /// </summary>\n    /// <param name=\"configuration\">The <see cref=\"IConfiguration\"/> instance to replace placeholders in.</param>\n    /// <returns>The given <see cref=\"IConfiguration\"/> instance.</returns>\n    public static IConfiguration ReplacePlaceholders(this IConfiguration configuration)\n    {\n        foreach (var kvp in configuration.AsEnumerable())\n        {\n            if (string.IsNullOrEmpty(kvp.Value))\n            {\n                // Skip empty configuration values.\n                continue;\n            }\n\n            // Replace placeholders in the configuration value.\n            var result = ConfigPlaceholderRegex.Replace(kvp.Value, match =>\n            {\n                if (!match.Success)\n                {\n                    // Return the original value.\n                    return kvp.Value;\n                }\n\n                if (match.Groups.Count != 2)\n                {\n                    // There is a match, but somehow no group for the placeholder.\n                    throw new InvalidConfigurationPlaceholderException(match.ToString());\n                }\n\n                var placeholder = match.Groups[1].Value;\n                if (placeholder.StartsWith(\":\") || placeholder.EndsWith(\":\"))\n                {\n                    // Placeholders cannot start or end with a colon.\n                    throw new InvalidConfigurationPlaceholderException(placeholder);\n                }\n\n                // Return the value in the configuration instance.\n                return configuration[placeholder]!;\n            });\n\n            // Replace the value in the configuration instance.\n            configuration[kvp.Key] = result;\n        }\n\n        return configuration;\n    }\n\n    private sealed class InvalidConfigurationPlaceholderException(string placeholder)\n        : InvalidOperationException($\"Invalid configuration placeholder: '{placeholder}'.\");\n\n    #endregion Placeholder\n\n    #region GetAppSetting\n\n    /// <summary>\n    /// GetRequiredAppSetting\n    /// Shorthand for GetSection(\"AppSettings\")[key]\n    /// </summary>\n    /// <param name=\"configuration\">IConfiguration instance</param>\n    /// <param name=\"key\">appSettings key</param>\n    /// <returns>app setting value</returns>\n    public static string GetRequiredAppSetting(this IConfiguration configuration, string key)\n    {\n        var value = configuration.GetAppSetting(key);\n        return Guard.NotNull(value, nameof(key));\n    }\n\n    /// <summary>\n    /// GetAppSetting\n    /// Shorthand for GetSection(\"AppSettings\")[key]\n    /// </summary>\n    /// <param name=\"configuration\">IConfiguration instance</param>\n    /// <param name=\"key\">appSettings key</param>\n    /// <returns>app setting value</returns>\n    public static string? GetAppSetting(this IConfiguration configuration, string key)\n    {\n        return configuration.GetSection(\"AppSettings\")?[key];\n    }\n\n    /// <summary>\n    /// GetAppSetting\n    /// Shorthand for GetSection(\"AppSettings\")[key]\n    /// </summary>\n    /// <param name=\"configuration\">IConfiguration instance</param>\n    /// <param name=\"name\">The connection string key</param>\n    /// <returns>The connection string value</returns>\n    public static string GetRequiredConnectionString(this IConfiguration configuration, string name)\n    {\n        var connString = configuration.GetConnectionString(name) ?? throw new InvalidOperationException(\"Connection string not exists\");\n        return connString;\n    }\n\n    /// <summary>\n    /// GetAppSetting\n    /// Shorthand for GetSection(\"AppSettings\")[key]\n    /// </summary>\n    /// <param name=\"configuration\">IConfiguration instance</param>\n    /// <param name=\"key\">appSettings key</param>\n    /// <param name=\"defaultValue\">defaultValue</param>\n    /// <returns>app setting value</returns>\n    public static string GetAppSetting(this IConfiguration configuration, string key, string defaultValue)\n    {\n        return configuration.GetAppSetting(key) ?? defaultValue;\n    }\n\n    /// <summary>\n    /// GetAppSetting\n    /// Shorthand for GetSection(\"AppSettings\")[key]\n    /// </summary>\n    /// <param name=\"configuration\">IConfiguration instance</param>\n    /// <param name=\"key\">appSettings key</param>\n    /// <returns>app setting value</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T GetAppSetting<T>(this IConfiguration configuration, string key)\n    {\n        return configuration.GetAppSetting(key).To<T>();\n    }\n\n    /// <summary>\n    /// GetAppSetting\n    /// Shorthand for GetSection(\"AppSettings\")[key]\n    /// </summary>\n    /// <param name=\"configuration\">IConfiguration instance</param>\n    /// <param name=\"key\">appSettings key</param>\n    /// <param name=\"defaultValue\">default value if not exist</param>\n    /// <returns>app setting value</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T GetAppSetting<T>(this IConfiguration configuration, string key, T defaultValue)\n    {\n        return configuration.GetAppSetting(key).ToOrDefault(defaultValue);\n    }\n\n    /// <summary>\n    /// GetAppSetting\n    /// Shorthand for GetSection(\"AppSettings\")[key]\n    /// </summary>\n    /// <param name=\"configuration\">IConfiguration instance</param>\n    /// <param name=\"key\">appSettings key</param>\n    /// <param name=\"defaultValueFunc\">default value func if not exist to get a default value</param>\n    /// <returns>app setting value</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T GetAppSetting<T>(this IConfiguration configuration, string key, Func<T> defaultValueFunc)\n    {\n        return configuration.GetAppSetting(key).ToOrDefault(defaultValueFunc);\n    }\n\n    #endregion GetAppSetting\n\n    #region FeatureFlag\n\n    /// <summary>\n    /// Feature flags configuration section name, FeatureFlags by default\n    /// </summary>\n    public static string FeatureFlagsSectionName = \"FeatureFlags\";\n\n    /// <summary>\n    /// FeatureFlag extension, (FeatureFlags:Feature) is feature enabled\n    /// </summary>\n    /// <param name=\"configuration\">configuration</param>\n    /// <param name=\"featureFlagName\">feature flag name</param>\n    /// <param name=\"featureFlagValue\">featureFlagValue value if config not exists</param>\n    /// <returns><c>true</c> enabled, otherwise disabled</returns>\n    public static bool TryGetFeatureFlagValue(this IConfiguration configuration, string featureFlagName, out bool featureFlagValue)\n    {\n        featureFlagValue = false;\n        var section = configuration.GetSection(FeatureFlagsSectionName);\n        if (section.Exists())\n        {\n            return bool.TryParse(section[featureFlagName], out featureFlagValue);\n        }\n        return false;\n    }\n\n    /// <summary>\n    /// FeatureFlag extension, (FeatureFlags:Feature) is feature enabled\n    /// </summary>\n    /// <param name=\"configuration\">configuration</param>\n    /// <param name=\"featureFlagName\">feature flag name</param>\n    /// <param name=\"defaultValue\">default value if config not exists</param>\n    /// <returns><c>true</c> enabled, otherwise disabled</returns>\n    public static bool IsFeatureEnabled(this IConfiguration configuration, string featureFlagName, bool defaultValue = false)\n    {\n        if (TryGetFeatureFlagValue(configuration, featureFlagName, out var value))\n        {\n            return value;\n        }\n        return defaultValue;\n    }\n\n    #endregion FeatureFlag\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/CoreExtension.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Collections.Concurrent;\nusing System.ComponentModel;\nusing System.Globalization;\nusing System.Reflection;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing WeihanLi.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class CoreExtension\n{\n    #region Boolean\n\n    /// <summary>\n    ///     A bool extension method that execute an Action if the value is true.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"action\">The action to execute.</param>\n    public static void IfTrue(this bool @this, Action? action)\n    {\n        if (@this)\n        {\n            action?.Invoke();\n        }\n    }\n\n    /// <summary>\n    ///     A bool extension method that execute an Action if the value is false.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"action\">The action to execute.</param>\n    public static void IfFalse(this bool @this, Action? action)\n    {\n        if (!@this)\n        {\n            action?.Invoke();\n        }\n    }\n\n    /// <summary>\n    ///     A bool extension method that show the trueValue when the @this value is true; otherwise show the falseValue.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"trueValue\">The true value to be returned if the @this value is true.</param>\n    /// <param name=\"falseValue\">The false value to be returned if the @this value is false.</param>\n    /// <returns>A string that represents of the current boolean value.</returns>\n    public static string ToString(this bool @this, string trueValue, string falseValue)\n    {\n        return @this ? trueValue : falseValue;\n    }\n\n    #endregion Boolean\n\n    #region Byte\n\n    /// <summary>\n    ///     Returns the larger of two 8-bit unsigned integers.\n    /// </summary>\n    /// <param name=\"val1\">The first of two 8-bit unsigned integers to compare.</param>\n    /// <param name=\"val2\">The second of two 8-bit unsigned integers to compare.</param>\n    /// <returns>Parameter  or , whichever is larger.</returns>\n    public static byte Max(this byte val1, byte val2)\n    {\n        return Math.Max(val1, val2);\n    }\n\n    /// <summary>\n    ///     Returns the smaller of two 8-bit unsigned integers.\n    /// </summary>\n    /// <param name=\"val1\">The first of two 8-bit unsigned integers to compare.</param>\n    /// <param name=\"val2\">The second of two 8-bit unsigned integers to compare.</param>\n    /// <returns>Parameter  or , whichever is smaller.</returns>\n    public static byte Min(this byte val1, byte val2)\n    {\n        return Math.Min(val1, val2);\n    }\n\n    #endregion Byte\n\n    #region ByteArray\n\n    /// <summary>\n    ///     Converts an array of 8-bit unsigned integers to its equivalent string representation that is encoded with\n    ///     base-64 digits.\n    /// </summary>\n    /// <param name=\"inArray\">An array of 8-bit unsigned integers.</param>\n    /// <returns>The string representation, in base 64, of the contents of .</returns>\n    public static string ToBase64String(this byte[] inArray)\n    {\n        return Convert.ToBase64String(inArray);\n    }\n\n    /// <summary>\n    ///     Converts an array of 8-bit unsigned integers to its equivalent string representation that is encoded with\n    ///     base-64 digits. A parameter specifies whether to insert line breaks in the return value.\n    /// </summary>\n    /// <param name=\"inArray\">An array of 8-bit unsigned integers.</param>\n    /// <param name=\"options\">to insert a line break every 76 characters, or  to not insert line breaks.</param>\n    /// <returns>The string representation in base 64 of the elements in .</returns>\n    public static string ToBase64String(this byte[] inArray, Base64FormattingOptions options)\n    {\n        return Convert.ToBase64String(inArray, options);\n    }\n\n    /// <summary>\n    ///     Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is\n    ///     encoded with base-64 digits. Parameters specify the subset as an offset in the input array, and the number of\n    ///     elements in the array to convert.\n    /// </summary>\n    /// <param name=\"inArray\">An array of 8-bit unsigned integers.</param>\n    /// <param name=\"offset\">An offset in .</param>\n    /// <param name=\"length\">The number of elements of  to convert.</param>\n    /// <returns>The string representation in base 64 of  elements of , starting at position .</returns>\n    public static string ToBase64String(this byte[] inArray, int offset, int length)\n    {\n        return Convert.ToBase64String(inArray, offset, length);\n    }\n\n    /// <summary>\n    ///     Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is\n    ///     encoded with base-64 digits. Parameters specify the subset as an offset in the input array, the number of\n    ///     elements in the array to convert, and whether to insert line breaks in the return value.\n    /// </summary>\n    /// <param name=\"inArray\">An array of 8-bit unsigned integers.</param>\n    /// <param name=\"offset\">An offset in .</param>\n    /// <param name=\"length\">The number of elements of  to convert.</param>\n    /// <param name=\"options\">to insert a line break every 76 characters, or  to not insert line breaks.</param>\n    /// <returns>The string representation in base 64 of  elements of , starting at position .</returns>\n    public static string ToBase64String(this byte[] inArray, int offset, int length, Base64FormattingOptions options)\n    {\n        return Convert.ToBase64String(inArray, offset, length, options);\n    }\n\n#if NET\n    public static string ToHexString(this ReadOnlySpan<byte> bytes, bool isLowerCase = false)\n    {\n#if NET9_0_OR_GREATER\n        return isLowerCase ? Convert.ToHexStringLower(bytes) : Convert.ToHexString(bytes);\n#else\n        return isLowerCase ? Convert.ToHexString(bytes).ToLowerInvariant() : Convert.ToHexString(bytes);\n#endif\n    }\n#endif\n\n    public static string ToHexString(this byte[] bytes, bool isLowerCase = false)\n    {\n#if NET9_0_OR_GREATER\n        return isLowerCase ? Convert.ToHexStringLower(bytes) : Convert.ToHexString(bytes);\n#elif NET5_0_OR_GREATER\n        return isLowerCase ? Convert.ToHexString(bytes).ToLowerInvariant() : Convert.ToHexString(bytes);\n#else\n        var bitString = BitConverter.ToString(bytes).Replace(\"-\", \"\");\n        return isLowerCase ? bitString.ToLowerInvariant() : bitString;\n#endif\n    }\n\n    /// <summary>\n    ///     A byte[] extension method that resizes the byte[].\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"newSize\">Size of the new.</param>\n    /// <returns>A byte[].</returns>\n    public static byte[] Resize(this byte[] @this, int newSize)\n    {\n        Array.Resize(ref @this, newSize);\n        return @this;\n    }\n\n    /// <summary>\n    ///     A byte[] extension method that converts the @this byteArray to a memory stream.\n    /// </summary>\n    /// <param name=\"byteArray\">The byteArray to act on</param>\n    /// <returns>@this as a MemoryStream.</returns>\n    public static MemoryStream ToMemoryStream(this byte[] byteArray)\n    {\n        return new(byteArray);\n    }\n\n    public static string GetString(this byte[]? byteArray)\n        => byteArray.HasValue() ? byteArray.GetString(Encoding.UTF8) : string.Empty;\n\n    public static string GetString(this byte[] byteArray, Encoding encoding) => encoding.GetString(byteArray);\n\n    #endregion ByteArray\n\n    #region Char\n\n    /// <summary>\n    ///     A char extension method that repeats a character the specified number of times.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"repeatCount\">Number of repeats.</param>\n    /// <returns>The repeated char.</returns>\n    public static string Repeat(this char @this, int repeatCount)\n    {\n        return new(@this, repeatCount);\n    }\n\n    /// <summary>\n    ///     Converts the specified numeric Unicode character to a double-precision floating point number.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to convert.</param>\n    /// <returns>The numeric value of  if that character represents a number; otherwise, -1.0.</returns>\n    public static double GetNumericValue(this char c)\n    {\n        return char.GetNumericValue(c);\n    }\n\n    /// <summary>\n    ///     Categorizes a specified Unicode character into a group identified by one of the  values.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to categorize.</param>\n    /// <returns>A  value that identifies the group that contains .</returns>\n    public static UnicodeCategory GetUnicodeCategory(this char c)\n    {\n        return char.GetUnicodeCategory(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as a control character.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is a control character; otherwise, false.</returns>\n    public static bool IsControl(this char c)\n    {\n        return char.IsControl(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as a decimal digit.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is a decimal digit; otherwise, false.</returns>\n    public static bool IsDigit(this char c)\n    {\n        return char.IsDigit(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as a Unicode letter.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is a letter; otherwise, false.</returns>\n    public static bool IsLetter(this char c)\n    {\n        return char.IsLetter(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as a letter or a decimal digit.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is a letter or a decimal digit; otherwise, false.</returns>\n    public static bool IsLetterOrDigit(this char c)\n    {\n        return char.IsLetterOrDigit(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as a lowercase letter.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is a lowercase letter; otherwise, false.</returns>\n    public static bool IsLower(this char c)\n    {\n        return char.IsLower(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as an uppercase letter.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is an uppercase letter; otherwise, false.</returns>\n    public static bool IsUpper(this char c)\n    {\n        return char.IsUpper(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as a number.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is a number; otherwise, false.</returns>\n    public static bool IsNumber(this char c)\n    {\n        return char.IsNumber(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as a separator character.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is a separator character; otherwise, false.</returns>\n    public static bool IsSeparator(this char c)\n    {\n        return char.IsSeparator(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as a symbol character.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is a symbol character; otherwise, false.</returns>\n    public static bool IsSymbol(this char c)\n    {\n        return char.IsSymbol(c);\n    }\n\n    /// <summary>\n    ///     Indicates whether the specified Unicode character is categorized as white space.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to evaluate.</param>\n    /// <returns>true if  is white space; otherwise, false.</returns>\n    public static bool IsWhiteSpace(this char c)\n    {\n        return char.IsWhiteSpace(c);\n    }\n\n    /// <summary>\n    ///     Converts the value of a specified Unicode character to its lowercase equivalent using specified culture-\n    ///     specific formatting information.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to convert.</param>\n    /// <param name=\"culture\">An object that supplies culture-specific casing rules.</param>\n    /// <returns>\n    ///     The lowercase equivalent of , modified according to , or the unchanged value of , if  is already lowercase or\n    ///     not alphabetic.\n    /// </returns>\n    public static char ToLower(this char c, CultureInfo culture)\n    {\n        return char.ToLower(c, culture);\n    }\n\n    /// <summary>\n    ///     Converts the value of a Unicode character to its lowercase equivalent.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to convert.</param>\n    /// <returns>\n    ///     The lowercase equivalent of , or the unchanged value of , if  is already lowercase or not alphabetic.\n    /// </returns>\n    public static char ToLower(this char c)\n    {\n        return char.ToLower(c);\n    }\n\n    /// <summary>\n    ///     Converts the value of a Unicode character to its lowercase equivalent using the casing rules of the invariant\n    ///     culture.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to convert.</param>\n    /// <returns>\n    ///     The lowercase equivalent of the  parameter, or the unchanged value of , if  is already lowercase or not\n    ///     alphabetic.\n    /// </returns>\n    public static char ToLowerInvariant(this char c)\n    {\n        return char.ToLowerInvariant(c);\n    }\n\n    /// <summary>\n    ///     Converts the value of a specified Unicode character to its uppercase equivalent using specified culture-\n    ///     specific formatting information.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to convert.</param>\n    /// <param name=\"culture\">An object that supplies culture-specific casing rules.</param>\n    /// <returns>\n    ///     The uppercase equivalent of , modified according to , or the unchanged value of  if  is already uppercase,\n    ///     has no uppercase equivalent, or is not alphabetic.\n    /// </returns>\n    public static char ToUpper(this char c, CultureInfo culture)\n    {\n        return char.ToUpper(c, culture);\n    }\n\n    /// <summary>\n    ///     Converts the value of a Unicode character to its uppercase equivalent.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to convert.</param>\n    /// <returns>\n    ///     The uppercase equivalent of , or the unchanged value of  if  is already uppercase, has no uppercase\n    ///     equivalent, or is not alphabetic.\n    /// </returns>\n    public static char ToUpper(this char c)\n    {\n        return char.ToUpper(c);\n    }\n\n    /// <summary>\n    ///     Converts the value of a Unicode character to its uppercase equivalent using the casing rules of the invariant\n    ///     culture.\n    /// </summary>\n    /// <param name=\"c\">The Unicode character to convert.</param>\n    /// <returns>\n    ///     The uppercase equivalent of the  parameter, or the unchanged value of , if  is already uppercase or not\n    ///     alphabetic.\n    /// </returns>\n    public static char ToUpperInvariant(this char c)\n    {\n        return char.ToUpperInvariant(c);\n    }\n\n    #endregion Char\n\n    #region DateTime\n\n    /// <summary>\n    ///     A DateTime extension method that ages the given this.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>An int.</returns>\n    public static int Age(this DateTime @this)\n    {\n        if (DateTime.Today.Month < @this.Month ||\n            DateTime.Today.Month == @this.Month &&\n            DateTime.Today.Day < @this.Day)\n        {\n            return DateTime.Today.Year - @this.Year - 1;\n        }\n        return DateTime.Today.Year - @this.Year;\n    }\n\n    /// <summary>\n    ///     A DateTime extension method that query if 'date' is date equal.\n    /// </summary>\n    /// <param name=\"date\">The date to act on.</param>\n    /// <param name=\"dateToCompare\">Date/Time of the date to compare.</param>\n    /// <returns>true if date equal, false if not.</returns>\n    public static bool IsDateEqual(this DateTime date, DateTime dateToCompare) => date.Date == dateToCompare.Date;\n\n    /// <summary>\n    ///     A DateTime extension method that query if '@this' is today.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if today, false if not.</returns>\n    public static bool IsToday(this DateTime @this)\n    {\n        return @this.Date == DateTime.Today;\n    }\n\n    /// <summary>\n    ///     A DateTime extension method that query if '@this' is a week day.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if '@this' is a week day, false if not.</returns>\n    public static bool IsWeekDay(this DateTime @this)\n    {\n        return !(@this.DayOfWeek == DayOfWeek.Saturday || @this.DayOfWeek == DayOfWeek.Sunday);\n    }\n\n    /// <summary>\n    ///     A DateTime extension method that query if '@this' is a week day.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if '@this' is a week day, false if not.</returns>\n    public static bool IsWeekendDay(this DateTime @this)\n    {\n        return @this.DayOfWeek == DayOfWeek.Saturday || @this.DayOfWeek == DayOfWeek.Sunday;\n    }\n\n    /// <summary>\n    ///     A DateTime extension method that return a DateTime with the time set to \"00:00:00:000\". The first moment of\n    ///     the day.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>A DateTime of the day with the time set to \"00:00:00:000\".</returns>\n    public static DateTime StartOfDay(this DateTime @this)\n    {\n        return new(@this.Year, @this.Month, @this.Day);\n    }\n\n    /// <summary>\n    ///     A DateTime extension method that return a DateTime of the first day of the month with the time set to\n    ///     \"00:00:00:000\". The first moment of the first day of the month.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>A DateTime of the first day of the month with the time set to \"00:00:00:000\".</returns>\n    public static DateTime StartOfMonth(this DateTime @this)\n    {\n        return new(@this.Year, @this.Month, 1);\n    }\n\n    /// <summary>\n    ///     A DateTime extension method that starts of week.\n    /// </summary>\n    /// <param name=\"dt\">The dt to act on.</param>\n    /// <param name=\"startDayOfWeek\">(Optional) the start day of week.</param>\n    /// <returns>A DateTime.</returns>\n    public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startDayOfWeek = DayOfWeek.Sunday)\n    {\n        var start = new DateTime(dt.Year, dt.Month, dt.Day);\n\n        if (start.DayOfWeek != startDayOfWeek)\n        {\n            var d = startDayOfWeek - start.DayOfWeek;\n            if (startDayOfWeek <= start.DayOfWeek)\n            {\n                return start.AddDays(d);\n            }\n            return start.AddDays(-7 + d);\n        }\n\n        return start;\n    }\n\n    /// <summary>\n    ///     A DateTime extension method that return a DateTime of the first day of the year with the time set to\n    ///     \"00:00:00:000\". The first moment of the first day of the year.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>A DateTime of the first day of the year with the time set to \"00:00:00:000\".</returns>\n    public static DateTime StartOfYear(this DateTime @this)\n    {\n        return new(@this.Year, 1, 1);\n    }\n\n    /// <summary>\n    ///     A DateTime extension method that converts the @this to an epoch time span.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as a TimeSpan.</returns>\n    public static TimeSpan ToEpochTimeSpan(this DateTime @this) => @this.ToUniversalTime().Subtract(new DateTime(1970, 1, 1));\n\n    /// <summary>\n    ///     A T extension method that check if the value is between inclusively the minValue and maxValue.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"minValue\">The minimum value.</param>\n    /// <param name=\"maxValue\">The maximum value.</param>\n    /// <returns>true if the value is between inclusively the minValue and maxValue, otherwise false.</returns>\n    public static bool InRange(this DateTime @this, DateTime minValue, DateTime maxValue)\n    {\n        return @this.CompareTo(minValue) >= 0 && @this.CompareTo(maxValue) <= 0;\n    }\n\n    /// <summary>\n    ///     Converts a time to the time in a particular time zone.\n    /// </summary>\n    /// <param name=\"dateTime\">The date and time to convert.</param>\n    /// <param name=\"destinationTimeZone\">The time zone to convert  to.</param>\n    /// <returns>The date and time in the destination time zone.</returns>\n    public static DateTime ConvertTime(this DateTime dateTime, TimeZoneInfo destinationTimeZone)\n    {\n        return TimeZoneInfo.ConvertTime(dateTime, destinationTimeZone);\n    }\n\n    /// <summary>\n    ///     Converts a time from one time zone to another.\n    /// </summary>\n    /// <param name=\"dateTime\">The date and time to convert.</param>\n    /// <param name=\"sourceTimeZone\">The time zone of .</param>\n    /// <param name=\"destinationTimeZone\">The time zone to convert  to.</param>\n    /// <returns>\n    ///     The date and time in the destination time zone that corresponds to the  parameter in the source time zone.\n    /// </returns>\n    public static DateTime ConvertTime(this DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone)\n    {\n        return TimeZoneInfo.ConvertTime(dateTime, sourceTimeZone, destinationTimeZone);\n    }\n\n    /// <summary>\n    ///     Converts a Coordinated Universal Time (UTC) to the time in a specified time zone.\n    /// </summary>\n    /// <param name=\"dateTime\">The Coordinated Universal Time (UTC).</param>\n    /// <param name=\"destinationTimeZone\">The time zone to convert  to.</param>\n    /// <returns>\n    ///     The date and time in the destination time zone. Its  property is  if  is ; otherwise, its  property is .\n    /// </returns>\n    public static DateTime ConvertTimeFromUtc(this DateTime dateTime, TimeZoneInfo destinationTimeZone)\n    {\n        return TimeZoneInfo.ConvertTimeFromUtc(dateTime, destinationTimeZone);\n    }\n\n    /// <summary>\n    ///     Converts the current date and time to Coordinated Universal Time (UTC).\n    /// </summary>\n    /// <param name=\"dateTime\">The date and time to convert.</param>\n    /// <returns>\n    ///     The Coordinated Universal Time (UTC) that corresponds to the  parameter. The  value&#39;s  property is always\n    ///     set to .\n    /// </returns>\n    public static DateTime ConvertTimeToUtc(this DateTime dateTime)\n    {\n        return TimeZoneInfo.ConvertTimeToUtc(dateTime);\n    }\n\n    /// <summary>\n    ///     Converts the time in a specified time zone to Coordinated Universal Time (UTC).\n    /// </summary>\n    /// <param name=\"dateTime\">The date and time to convert.</param>\n    /// <param name=\"sourceTimeZone\">The time zone of .</param>\n    /// <returns>\n    ///     The Coordinated Universal Time (UTC) that corresponds to the  parameter. The  object&#39;s  property is\n    ///     always set to .\n    /// </returns>\n    public static DateTime ConvertTimeToUtc(this DateTime dateTime, TimeZoneInfo sourceTimeZone)\n    {\n        return TimeZoneInfo.ConvertTimeToUtc(dateTime, sourceTimeZone);\n    }\n\n    /// <summary>\n    /// ToDateString(\"yyyy-MM-dd\")\n    /// </summary>\n    /// <param name=\"this\">dateTime</param>\n    /// <param name=\"format\">date format, using `yyyy-MM-dd` by default</param>\n    /// <returns>The formatted date string</returns>\n    public static string ToDateString(this DateTime @this, string format = \"yyyy-MM-dd\")\n    {\n        return @this.ToString(Guard.NotNull(format));\n    }\n\n    /// <summary>\n    /// ToTimeString(\"yyyy-MM-dd HH:mm:ss\")\n    /// </summary>\n    /// <param name=\"this\">dateTime</param>\n    /// <param name=\"format\">dateTime format, using `yyyy-MM-dd HH:mm:ss` by default</param>\n    /// <returns>The formatted time string</returns>\n    public static string ToTimeString(this DateTime @this, string format = \"yyyy-MM-dd HH:mm:ss\")\n    {\n        return @this.ToString(Guard.NotNull(format));\n    }\n\n    #endregion DateTime\n\n    #region Double\n\n    /// <summary>\n    ///     Returns the smallest integral value that is greater than or equal to the specified double-precision floating-\n    ///     point number.\n    /// </summary>\n    /// <param name=\"a\">A double-precision floating-point number.</param>\n    /// <returns>\n    ///     The smallest integral value that is greater than or equal to . If  is equal to , , or , that value is\n    ///     returned. Note that this method returns a  instead of an integral type.\n    /// </returns>\n    public static int Ceiling(this double a) => Convert.ToInt32(Math.Ceiling(a));\n\n    /// <summary>\n    ///     Returns the natural (base e) logarithm of a specified number.\n    /// </summary>\n    /// <param name=\"d\">The number whose logarithm is to be found.</param>\n    /// <returns>\n    ///     One of the values in the following table.  parameterReturn value Positive The natural logarithm of ; that is,\n    ///     ln , or log eZero Negative Equal to Equal to.\n    /// </returns>\n    public static double Log(this double d)\n    {\n        return Math.Log(d);\n    }\n\n    /// <summary>\n    ///     Returns the base 10 logarithm of a specified number.\n    /// </summary>\n    /// <param name=\"d\">A number whose logarithm is to be found.</param>\n    /// <returns>\n    ///     One of the values in the following table.  parameter Return value Positive The base 10 log of ; that is, log\n    ///     10. Zero Negative Equal to Equal to.\n    /// </returns>\n    public static double Log10(this double d)\n    {\n        return Math.Log10(d);\n    }\n\n    #endregion Double\n\n    #region Enum\n\n    /// <summary>\n    /// A T extension method to determines whether the object is equal to any of the provided values.\n    /// </summary>\n    /// <param name=\"this\">The object to be compared.</param>\n    /// <param name=\"values\">The value list to compare with the object.</param>\n    /// <returns>true if the values list contains the object, else false.</returns>\n    public static bool In(this Enum @this, params Enum[] values)\n    {\n        return Array.IndexOf(values, @this) >= 0;\n    }\n\n    /// <summary>\n    /// An object extension method that gets description attribute.\n    /// </summary>\n    /// <param name=\"value\">The value to act on.</param>\n    /// <returns>The description attribute.</returns>\n    public static string GetDescription(this Enum value)\n    {\n        var stringValue = value.ToString();\n        var attr = value.GetType().GetField(stringValue)?\n            .GetCustomAttribute<DescriptionAttribute>();\n        return attr?.Description ?? stringValue;\n    }\n\n    #endregion Enum\n\n    #region Guid\n\n    /// <summary>A GUID extension method that query if '@this' is empty.</summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if empty, false if not.</returns>\n    public static bool IsNullOrEmpty(this Guid? @this)\n    {\n        return !@this.HasValue || @this == Guid.Empty;\n    }\n\n    /// <summary>A GUID extension method that query if '@this' is not null or empty.</summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if empty, false if not.</returns>\n    public static bool IsNotNullOrEmpty(this Guid? @this)\n    {\n        return @this.HasValue && @this.Value != Guid.Empty;\n    }\n\n    /// <summary>A GUID extension method that query if '@this' is empty.</summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if empty, false if not.</returns>\n    public static bool IsEmpty(this Guid @this)\n    {\n        return @this == Guid.Empty;\n    }\n\n    /// <summary>A GUID extension method that queries if a not is empty.</summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if a not is empty, false if not.</returns>\n    public static bool IsNotEmpty(this Guid @this)\n    {\n        return @this != Guid.Empty;\n    }\n\n    #endregion Guid\n\n    #region int\n\n    /// <summary>\n    ///     An Int32 extension method that factor of.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"factorNumber\">The factor number.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool FactorOf(this int @this, int factorNumber)\n    {\n        return factorNumber % @this == 0;\n    }\n\n    /// <summary>\n    ///     An Int32 extension method that query if '@this' is even.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if even, false if not.</returns>\n    public static bool IsEven(this int @this)\n    {\n        return @this % 2 == 0;\n    }\n\n    /// <summary>\n    ///     An Int32 extension method that query if '@this' is odd.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if odd, false if not.</returns>\n    public static bool IsOdd(this int @this)\n    {\n        return @this % 2 != 0;\n    }\n\n    /// <summary>\n    ///     An Int32 extension method that query if '@this' is multiple of.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"factor\">The factor.</param>\n    /// <returns>true if multiple of, false if not.</returns>\n    public static bool IsMultipleOf(this int @this, int factor)\n    {\n        return @this % factor == 0;\n    }\n\n    /// <summary>\n    ///     An Int32 extension method that query if '@this' is prime.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if prime, false if not.</returns>\n    public static bool IsPrime(this int @this)\n    {\n        if (@this == 1 || @this == 2)\n        {\n            return true;\n        }\n\n        if (@this % 2 == 0)\n        {\n            return false;\n        }\n\n        var sqrt = (int)Math.Sqrt(@this);\n        for (var t = 3; t <= sqrt; t = t + 2)\n        {\n            if (@this % t == 0)\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /// <summary>\n    ///     Returns the specified 32-bit signed integer value as an array of bytes.\n    /// </summary>\n    /// <param name=\"value\">The number to convert.</param>\n    /// <returns>An array of bytes with length 4.</returns>\n    public static byte[] GetBytes(this int value)\n    {\n        return BitConverter.GetBytes(value);\n    }\n\n    /// <summary>\n    /// Ensures the exitCode success\n    /// </summary>\n    /// <param name=\"exitCode\">exitCode</param>\n    /// <param name=\"successCode\">successCode</param>\n    /// <returns></returns>\n    /// <exception cref=\"InvalidOperationException\">Exception when exitCode not match the successCode</exception>\n    [System.Diagnostics.DebuggerStepThrough]\n    public static int EnsureSuccessExitCode(this int exitCode, int successCode = 0)\n    {\n        if (exitCode != 0)\n            throw new InvalidOperationException($\"Unexpected exit code:{exitCode}\");\n\n        return exitCode;\n    }\n\n    #endregion int\n\n    #region object\n\n    /// <summary>\n    ///     An object extension method that converts the @this to an or default.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>A T.</returns>\n    public static T? AsOrDefault<T>(this object? @this)\n    {\n        if (@this is null)\n        {\n            return default;\n        }\n        try\n        {\n            return (T)@this;\n        }\n        catch\n        {\n            return default;\n        }\n    }\n\n    /// <summary>\n    ///     An object extension method that converts the @this to an or default.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"defaultValue\">The default value.</param>\n    /// <returns>A T.</returns>\n    public static T AsOrDefault<T>(this object? @this, T defaultValue)\n    {\n        if (@this is null)\n        {\n            return defaultValue;\n        }\n        try\n        {\n            return (T)@this;\n        }\n        catch (Exception)\n        {\n            return defaultValue;\n        }\n    }\n\n    /// <summary>\n    ///     An object extension method that converts the @this to an or default.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"defaultValueFactory\">The default value factory.</param>\n    /// <returns>A T.</returns>\n    public static T AsOrDefault<T>(this object? @this, Func<T> defaultValueFactory)\n    {\n        if (@this is null)\n        {\n            return defaultValueFactory();\n        }\n        try\n        {\n            return (T)@this;\n        }\n        catch (Exception)\n        {\n            return defaultValueFactory();\n        }\n    }\n\n    /// <summary>\n    ///     A System.Object extension method that toes the given this.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">this.</param>\n    /// <returns>A T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T To<T>(this object? @this)\n    {\n#nullable disable\n\n        if (@this == null || @this == DBNull.Value)\n        {\n            return (T)(object)null;\n        }\n#nullable restore\n\n        var targetType = typeof(T).Unwrap();\n        var sourceType = @this.GetType().Unwrap();\n        if (sourceType == targetType)\n        {\n            return (T)@this;\n        }\n        var converter = TypeDescriptor.GetConverter(sourceType);\n        if (converter.CanConvertTo(targetType))\n        {\n            return (T)converter.ConvertTo(@this, targetType)!;\n        }\n\n        converter = TypeDescriptor.GetConverter(targetType);\n        if (converter.CanConvertFrom(sourceType))\n        {\n            return (T)converter.ConvertFrom(@this)!;\n        }\n\n        return (T)Convert.ChangeType(@this, targetType);\n    }\n\n    /// <summary>\n    ///     A System.Object extension method that toes the given this.\n    /// </summary>\n    /// <param name=\"this\">this.</param>\n    /// <param name=\"type\">The type.</param>\n    /// <returns>An object.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static object? To(this object? @this, Type type)\n    {\n        if (@this == null || @this == DBNull.Value)\n        {\n            return null;\n        }\n\n        var targetType = type.Unwrap();\n        var sourceType = @this.GetType().Unwrap();\n\n        if (sourceType == targetType)\n        {\n            return @this;\n        }\n\n        var converter = TypeDescriptor.GetConverter(sourceType);\n        if (converter.CanConvertTo(targetType))\n        {\n            return converter.ConvertTo(@this, targetType);\n        }\n\n        converter = TypeDescriptor.GetConverter(targetType);\n        if (converter.CanConvertFrom(sourceType))\n        {\n            return converter.ConvertFrom(@this);\n        }\n\n        return Convert.ChangeType(@this, targetType);\n    }\n\n    /// <summary>\n    ///     A System.Object extension method that converts this object to an or default.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">this.</param>\n    /// <param name=\"defaultValueFactory\">The default value factory.</param>\n    /// <returns>The given data converted to a T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T ToOrDefault<T>(this object? @this, Func<object?, T> defaultValueFactory)\n    {\n        try\n        {\n            return (T)@this.To(typeof(T))!;\n        }\n        catch (Exception)\n        {\n            return defaultValueFactory(@this);\n        }\n    }\n\n    /// <summary>\n    ///     A System.Object extension method that converts this object to an or default.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">this.</param>\n    /// <param name=\"defaultValueFactory\">The default value factory.</param>\n    /// <returns>The given data converted to a T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T ToOrDefault<T>(this object? @this, Func<T> defaultValueFactory)\n    {\n        return @this.ToOrDefault(_ => defaultValueFactory());\n    }\n\n    /// <summary>\n    ///     A System.Object extension method that converts this object to an or default.\n    /// </summary>\n    /// <param name=\"this\">this.</param>\n    /// <param name=\"type\">type</param>\n    /// <returns>The given data converted to</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static object? ToOrDefault(this object? @this, Type type)\n    {\n        Guard.NotNull(type, nameof(type));\n        try\n        {\n            return @this.To(type);\n        }\n        catch (Exception)\n        {\n            return type.GetDefaultValue();\n        }\n    }\n\n    /// <summary>\n    ///     A System.Object extension method that converts this object to an or default.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">this.</param>\n    /// <returns>The given data converted to a T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T? ToOrDefault<T>(this object? @this)\n    {\n        return @this.ToOrDefault(_ => default(T));\n    }\n\n    /// <summary>\n    ///     A System.Object extension method that converts this object to an or default.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">this.</param>\n    /// <param name=\"defaultValue\">The default value.</param>\n    /// <returns>The given data converted to a T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T ToOrDefault<T>(this object? @this, T defaultValue)\n    {\n        return @this.ToOrDefault(_ => defaultValue);\n    }\n\n#if NET    \n    /// <summary>\n    /// Parse ReadOnlySpan to specific type instance\n    /// </summary>\n    /// <param name=\"this\">span text</param>\n    /// <param name=\"formatProvider\">An object that provides culture-specific formatting information</param>\n    /// <typeparam name=\"T\">The destination type</typeparam>\n    /// <returns>The parsed value of type T.</returns>\n    public static T To<T>(this ReadOnlySpan<char> @this, IFormatProvider? formatProvider = null) \n        where T: ISpanParsable<T>\n    {\n        return T.Parse(@this, formatProvider);\n    }\n\n    /// <summary>\n    /// Parse ReadOnlySpan to specific type instance\n    /// </summary>\n    /// <param name=\"this\">span text</param>\n    /// <param name=\"formatProvider\">An object that provides culture-specific formatting information</param>\n    /// <param name=\"defaultValue\">default value</param>\n    /// <typeparam name=\"T\">The destination type</typeparam>\n    /// <returns>The parsed value of type T, or the default value if parsing fails.</returns>\n    public static T? ToOrDefault<T>(this ReadOnlySpan<char> @this, \n        IFormatProvider? formatProvider = null, \n        T? defaultValue = default) \n        where T: ISpanParsable<T>\n    {\n        return T.TryParse(@this, formatProvider, out var result) ? result : defaultValue;\n    }\n#endif\n\n    /// <summary>\n    ///     A T extension method that chains actions.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"action\">The action.</param>\n    /// <returns>The @this acted on.</returns>\n    public static T Chain<T>(this T @this, Action<T>? action)\n    {\n        action?.Invoke(@this);\n\n        return @this;\n    }\n\n    /// <summary>\n    ///     A T extension method that gets value or default.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <typeparam name=\"TResult\">Type of the result.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"func\">The function.</param>\n    /// <returns>The value or default.</returns>\n    public static TResult? GetValueOrDefault<T, TResult>(this T @this, Func<T, TResult> func)\n    {\n        try\n        {\n            return func(@this);\n        }\n        catch (Exception)\n        {\n            return default;\n        }\n    }\n\n    /// <summary>\n    ///     A T extension method that gets value or default.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <typeparam name=\"TResult\">Type of the result.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"func\">The function.</param>\n    /// <param name=\"defaultValue\">The default value.</param>\n    /// <returns>The value or default.</returns>\n    public static TResult GetValueOrDefault<T, TResult>(this T @this, Func<T, TResult> func, TResult defaultValue)\n    {\n        try\n        {\n            return func(@this);\n        }\n        catch (Exception)\n        {\n            return defaultValue;\n        }\n    }\n\n    /// <summary>A TType extension method that tries.</summary>\n    /// <typeparam name=\"TType\">Type of the type.</typeparam>\n    /// <typeparam name=\"TResult\">Type of the result.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"tryFunction\">The try function.</param>\n    /// <param name=\"catchValue\">The catch value.</param>\n    /// <returns>A TResult.</returns>\n    public static TResult Try<TType, TResult>(this TType @this, Func<TType, TResult> tryFunction, TResult catchValue)\n    {\n        try\n        {\n            return tryFunction(@this);\n        }\n        catch\n        {\n            return catchValue;\n        }\n    }\n\n    /// <summary>A TType extension method that tries.</summary>\n    /// <typeparam name=\"TType\">Type of the type.</typeparam>\n    /// <typeparam name=\"TResult\">Type of the result.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"tryFunction\">The try function.</param>\n    /// <param name=\"catchValueFactory\">The catch value factory.</param>\n    /// <returns>A TResult.</returns>\n    public static TResult Try<TType, TResult>(this TType @this, Func<TType, TResult> tryFunction, Func<TType, TResult> catchValueFactory)\n    {\n        try\n        {\n            return tryFunction(@this);\n        }\n        catch\n        {\n            return catchValueFactory(@this);\n        }\n    }\n\n    /// <summary>A TType extension method that tries.</summary>\n    /// <typeparam name=\"TType\">Type of the type.</typeparam>\n    /// <typeparam name=\"TResult\">Type of the result.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"tryFunction\">The try function.</param>\n    /// <param name=\"result\">[out] The result.</param>\n    /// <returns>A TResult.</returns>\n    public static bool Try<TType, TResult>(this TType @this, Func<TType, TResult> tryFunction, out TResult? result)\n    {\n        try\n        {\n            result = tryFunction(@this);\n            return true;\n        }\n        catch\n        {\n            result = default;\n            return false;\n        }\n    }\n\n    /// <summary>A TType extension method that tries.</summary>\n    /// <typeparam name=\"TType\">Type of the type.</typeparam>\n    /// <typeparam name=\"TResult\">Type of the result.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"tryFunction\">The try function.</param>\n    /// <param name=\"catchValue\">The catch value.</param>\n    /// <param name=\"result\">[out] The result.</param>\n    /// <returns>A TResult.</returns>\n    public static bool Try<TType, TResult>(this TType @this, Func<TType, TResult> tryFunction, TResult catchValue, out TResult result)\n    {\n        try\n        {\n            result = tryFunction(@this);\n            return true;\n        }\n        catch\n        {\n            result = catchValue;\n            return false;\n        }\n    }\n\n    /// <summary>A TType extension method that tries.</summary>\n    /// <typeparam name=\"TType\">Type of the type.</typeparam>\n    /// <typeparam name=\"TResult\">Type of the result.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"tryFunction\">The try function.</param>\n    /// <param name=\"catchValueFactory\">The catch value factory.</param>\n    /// <param name=\"result\">[out] The result.</param>\n    /// <returns>A TResult.</returns>\n    public static bool Try<TType, TResult>(this TType @this, Func<TType, TResult> tryFunction, Func<TType, TResult> catchValueFactory, out TResult result)\n    {\n        try\n        {\n            result = tryFunction(@this);\n            return true;\n        }\n        catch\n        {\n            result = catchValueFactory(@this);\n            return false;\n        }\n    }\n\n    /// <summary>A TType extension method that attempts to action from the given data.</summary>\n    /// <typeparam name=\"TType\">Type of the type.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"tryAction\">The try action.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool Try<TType>(this TType @this, Action<TType> tryAction)\n    {\n        try\n        {\n            tryAction(@this);\n            return true;\n        }\n        catch\n        {\n            return false;\n        }\n    }\n\n    /// <summary>A TType extension method that attempts to action from the given data.</summary>\n    /// <typeparam name=\"TType\">Type of the type.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"tryAction\">The try action.</param>\n    /// <param name=\"catchAction\">The catch action.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool Try<TType>(this TType @this, Action<TType> tryAction, Action<TType> catchAction)\n    {\n        try\n        {\n            tryAction(@this);\n            return true;\n        }\n        catch\n        {\n            catchAction(@this);\n            return false;\n        }\n    }\n\n    /// <summary>\n    ///     A T extension method that check if the value is between inclusively the minValue and maxValue.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"minValue\">The minimum value.</param>\n    /// <param name=\"maxValue\">The maximum value.</param>\n    /// <returns>true if the value is between inclusively the minValue and maxValue, otherwise false.</returns>\n    public static bool InRange<T>(this T @this, T minValue, T maxValue) where T : IComparable<T>\n    {\n        return @this.CompareTo(minValue) >= 0 && @this.CompareTo(maxValue) <= 0;\n    }\n\n    /// <summary>\n    ///     A T extension method that query if 'source' is the default value.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"source\">The source to act on.</param>\n    /// <returns>true if default, false if not.</returns>\n    public static bool IsDefault<T>(this T source)\n    {\n        return source is null || source.Equals(default(T));\n    }\n\n    /// <summary>\n    /// Get param dictionary\n    /// </summary>\n    public static IDictionary<string, object?> ParseParamDictionary(this object? paramInfo)\n    {\n        var paramDic = new Dictionary<string, object?>();\n        if (paramInfo is null)\n        {\n            return paramDic;\n        }\n\n        var type = paramInfo.GetType();\n        if (type.IsValueTuple()) // Tuple\n        {\n            var fields = CacheUtil.GetTypeFields(type);\n            foreach (var field in fields)\n            {\n                paramDic[field.Name] = field.GetValue(paramInfo);\n            }\n        }\n        else if (paramInfo is IDictionary<string, object?> paramDictionary)\n        {\n            return paramDictionary;\n        }\n        else // get properties\n        {\n            var properties = CacheUtil.GetTypeProperties(type);\n            foreach (var property in properties)\n            {\n                if (property.CanRead)\n                {\n                    paramDic[property.Name] = property.GetValueGetter()?.Invoke(paramInfo);\n                }\n            }\n        }\n\n        return paramDic;\n    }\n    #endregion object\n\n    #region Random\n\n    /// <summary>\n    ///     A Random extension method that return a random value from the specified values.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"values\">A variable-length parameters list containing arguments.</param>\n    /// <returns>One of the specified value.</returns>\n    public static T OneOf<T>(this Random @this, params T[] values)\n    {\n        return values[@this.Next(values.Length)];\n    }\n\n    /// <summary>\n    ///     A Random extension method that flip a coin toss.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true 50% of time, otherwise false.</returns>\n    public static bool CoinToss(this Random @this)\n    {\n        return @this.Next(2) == 0;\n    }\n\n    #endregion Random\n\n    #region string\n\n    /// <summary>\n    ///     A string extension method that query if '@this' is null or empty.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if null or empty, false if not.</returns>\n    public static bool IsNullOrEmpty([NotNullWhen(false)] this string? @this) => string.IsNullOrEmpty(@this);\n\n    /// <summary>\n    ///     A string extension method that query if '@this' is not null and not empty.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>false if null or empty, true if not.</returns>\n    public static bool IsNotNullOrEmpty([NotNullWhen(true)] this string? @this) => !string.IsNullOrEmpty(@this);\n\n    /// <summary>\n    ///     A string extension method that query if '@this' is null or whiteSpace.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>true if null or whiteSpace, false if not.</returns>\n    public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? @this) => string.IsNullOrWhiteSpace(@this);\n\n    /// <summary>\n    ///     A string extension method that query if '@this' is not null and not whiteSpace.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>false if null or whiteSpace, true if not.</returns>\n    public static bool IsNotNullOrWhiteSpace([NotNullWhen(true)] this string? @this) => !string.IsNullOrWhiteSpace(@this);\n\n    /// <summary>\n    ///     Concatenates the elements of an object array, using the specified separator between each element.\n    /// </summary>\n    /// <param name=\"separator\">\n    ///     The string to use as a separator.  is included in the returned string only if  has more\n    ///     than one element.\n    /// </param>\n    /// <param name=\"values\">An array that contains the elements to concatenate.</param>\n    /// <returns>\n    ///     A string that consists of the elements of  delimited by the  string. If  is an empty array, the method\n    ///     returns .\n    /// </returns>\n    public static string Join<T>(this string separator, IEnumerable<T> values) => string.Join(separator, values);\n\n    /// <summary>\n    ///     Indicates whether the specified regular expression finds a match in the specified input string.\n    /// </summary>\n    /// <param name=\"input\">The string to search for a match.</param>\n    /// <param name=\"pattern\">The regular expression pattern to match.</param>\n    /// <returns>true if the regular expression finds a match; otherwise, false.</returns>\n    public static bool IsMatch(this string input, string pattern) => Regex.IsMatch(input, pattern);\n\n    /// <summary>\n    ///     Indicates whether the specified regular expression finds a match in the specified input string, using the\n    ///     specified matching options.\n    /// </summary>\n    /// <param name=\"input\">The string to search for a match.</param>\n    /// <param name=\"pattern\">The regular expression pattern to match.</param>\n    /// <param name=\"options\">A bitwise combination of the enumeration values that provide options for matching.</param>\n    /// <returns>true if the regular expression finds a match; otherwise, false.</returns>\n    public static bool IsMatch(this string input, string pattern, RegexOptions options) => Regex.IsMatch(input, pattern, options);\n\n    /// <summary>An IEnumerable&lt;string&gt; extension method that concatenates the given this.</summary>\n    /// <param name=\"stringCollection\">The string collection to act on.</param>\n    /// <returns>A string.</returns>\n    public static string Concatenate(this IEnumerable<string> stringCollection)\n    {\n        return string.Join(string.Empty, stringCollection);\n    }\n\n    /// <summary>An IEnumerable&lt;T&gt; extension method that concatenates.</summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"source\">The source to act on.</param>\n    /// <param name=\"func\">The function.</param>\n    /// <returns>A string.</returns>\n    public static string Concatenate<T>(this IEnumerable<T> source, Func<T, string> func)\n    {\n        return string.Join(string.Empty, source.Select(func));\n    }\n\n    /// <summary>\n    ///     A string extension method that query if this object contains the given value.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"value\">The value.</param>\n    /// <returns>true if the value is in the string, false if not.</returns>\n    public static bool Contains(this string @this, string value) => Guard.NotNull(@this).IndexOf(value, StringComparison.Ordinal) != -1;\n\n    /// <summary>\n    ///     A string extension method that query if this object contains the given value.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"value\">The value.</param>\n    /// <param name=\"comparisonType\">Type of the comparison.</param>\n    /// <returns>true if the value is in the string, false if not.</returns>\n    public static bool Contains(this string @this, string value, StringComparison comparisonType) => Guard.NotNull(@this).IndexOf(value, comparisonType) != -1;\n\n    /// <summary>\n    ///     A string extension method that extracts this object.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"predicate\">The predicate.</param>\n    /// <returns>A string.</returns>\n    public static string Extract(this string @this, Func<char, bool> predicate) => new(Guard.NotNull(@this).ToCharArray().Where(predicate).ToArray());\n\n    /// <summary>\n    ///     A string extension method that removes the letter.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"predicate\">The predicate.</param>\n    /// <returns>A string.</returns>\n    public static string RemoveWhere(this string @this, Func<char, bool> predicate) => new(Guard.NotNull(@this).ToCharArray().Where(x => !predicate(x)).ToArray());\n\n    /// <summary>\n    ///     Replaces the format item in a specified String with the text equivalent of the value of a corresponding\n    ///     Object instance in a specified array.\n    /// </summary>\n    /// <param name=\"this\">A String containing zero or more format items.</param>\n    /// <param name=\"values\">An Object array containing zero or more objects to format.</param>\n    /// <returns>\n    ///     A copy of format in which the format items have been replaced by the String equivalent of the corresponding\n    ///     instances of Object in args.\n    /// </returns>\n    public static string FormatWith(this string @this, params object?[] values) => string.Format(Guard.NotNull(@this), values);\n\n    /// <summary>\n    ///     A string extension method that query if '@this' satisfies the specified pattern.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"pattern\">The pattern to use. Use '*' as a wildcard string.</param>\n    /// <returns>true if '@this' satisfies the specified pattern, false if not.</returns>\n    public static bool IsLike(this string @this, string pattern)\n    {\n        // Turn the pattern into a regex pattern, and match the whole string with ^$\n        var regexPattern = \"^\" + Regex.Escape(pattern) + \"$\";\n\n        // Escape special character ?, #, *, [], and [!]\n        regexPattern = regexPattern.Replace(@\"\\[!\", \"[^\")\n            .Replace(@\"\\[\", \"[\")\n            .Replace(@\"\\]\", \"]\")\n            .Replace(@\"\\?\", \".\")\n            .Replace(@\"\\*\", \".*\")\n            .Replace(@\"\\#\", @\"\\d\");\n\n        return Regex.IsMatch(Guard.NotNull(@this), regexPattern);\n    }\n\n    /// <summary>\n    /// SafeSubstring\n    /// </summary>\n    /// <param name=\"this\"></param>\n    /// <param name=\"startIndex\"></param>\n    /// <returns></returns>\n    public static string SafeSubstring(this string @this, int startIndex)\n    {\n        if (startIndex < 0 || startIndex > @this.Length)\n        {\n            return string.Empty;\n        }\n        return @this.Substring(startIndex);\n    }\n\n    /// <summary>\n    /// SafeSubstring\n    /// </summary>\n    /// <param name=\"str\"></param>\n    /// <param name=\"startIndex\"></param>\n    /// <param name=\"length\"></param>\n    /// <returns></returns>\n    public static string SafeSubstring(this string str, int startIndex, int length)\n    {\n        Guard.NotNull(str, nameof(str));\n        if (startIndex < 0 || startIndex >= str.Length || length < 0)\n        {\n            return string.Empty;\n        }\n        return str.Substring(startIndex, Math.Min(str.Length - startIndex, length));\n    }\n\n    /// <summary>\n    /// Sub, not only substring but support for negative numbers\n    /// </summary>\n    /// <param name=\"this\">string to be handled</param>\n    /// <param name=\"startIndex\">startIndex to sub-stract</param>\n    /// <returns>substring</returns>\n    public static string Sub(this string @this, int startIndex)\n    {\n        Guard.NotNull(@this);\n        if (startIndex >= 0)\n        {\n            return @this.SafeSubstring(startIndex);\n        }\n        if (Math.Abs(startIndex) > @this.Length)\n        {\n            return string.Empty;\n        }\n        return @this.Substring(@this.Length + startIndex);\n    }\n\n    /// <summary>\n    ///     A string extension method that repeats the string a specified number of times.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"repeatCount\">Number of repeats.</param>\n    /// <returns>The repeated string.</returns>\n    public static string Repeat(this string @this, int repeatCount)\n    {\n        Guard.NotNull(@this);\n        if (@this.Length == 1)\n        {\n            return new string(@this[0], repeatCount);\n        }\n\n        var sb = new StringBuilder(repeatCount * @this.Length);\n        while (repeatCount-- > 0)\n        {\n            sb.Append(@this);\n        }\n\n        return sb.ToString();\n    }\n\n    /// <summary>\n    ///     A string extension method that reverses the given string.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The string reversed.</returns>\n    public static string Reverse(this string? @this)\n    {\n        if (@this.IsNullOrWhiteSpace())\n        {\n            return @this ?? string.Empty;\n        }\n\n        var chars = @this.ToCharArray();\n        Array.Reverse(chars);\n        return new string(chars);\n    }\n\n    /// <summary>\n    ///     Returns a String array containing the substrings in this string that are delimited by elements of a specified\n    ///     String array. A parameter specifies whether to return empty array elements.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"separator\">A string that delimit the substrings in this string.</param>\n    /// <param name=\"option\">\n    ///     (Optional) Specify RemoveEmptyEntries to omit empty array elements from the array returned,\n    ///     or None to include empty array elements in the array returned.\n    /// </param>\n    /// <returns>\n    ///     An array whose elements contain the substrings in this string that are delimited by the separator.\n    /// </returns>\n    public static string[] Split(this string @this, string separator, StringSplitOptions option = StringSplitOptions.None)\n        => Guard.NotNull(@this).Split(new[] { separator }, option);\n\n    /// <summary>\n    ///     A string extension method that converts the @this to a byte array.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as a byte[].</returns>\n    public static byte[] ToBytes(this string @this) => Encoding.UTF8.GetBytes(Guard.NotNull(@this));\n\n    /// <summary>\n    ///     A string extension method that converts the @this to a byte array.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"encoding\">encoding</param>\n    /// <returns>@this as a byte[].</returns>\n    public static byte[] ToBytes(this string @this, Encoding encoding) => encoding.GetBytes(Guard.NotNull(@this));\n\n    public static byte[] HexStringToBytes(this string hexString)\n    {\n        if (string.IsNullOrEmpty(hexString))\n            return [];\n\n#if NET\n        return Convert.FromHexString(hexString);\n#else\n        var charArray = hexString.ToCharArray();\n        var bytes = new byte[charArray.Length / 2];\n        for (var i = 0; i < bytes.Length; i++)\n        {\n            var n = Convert.ToInt32(new string([charArray[i * 2], charArray[i * 2 + 1]]), 16);\n            bytes[i] = (byte)n;\n        }\n        return bytes;\n#endif\n    }\n\n    public static byte[] GetBytes(this string str) => Guard.NotNull(str, nameof(str)).GetBytes(null);\n\n    public static byte[] GetBytes(this string str, Encoding? encoding) => (encoding ?? Encoding.UTF8).GetBytes(Guard.NotNull(str, nameof(str)));\n\n    public static bool ToBoolean(this string? value, bool defaultValue = false)\n    {\n        return value switch\n        {\n            null => defaultValue,\n            \"\" or \"1\" or \"y\" or \"yes\" => true,\n            \"0\" or \"n\" or \"no\" => false,\n            _ => bool.TryParse(value, out var val) ? val : defaultValue\n        };\n    }\n\n    /// <summary>\n    ///     A string extension method that converts the @this to an enum.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as a T.</returns>\n    public static T ToEnum<T>(this string @this) => (T)Enum.Parse(typeof(T), Guard.NotNull(@this));\n\n    /// <summary>\n    ///     A string extension method that converts the @this to a title case.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as a string.</returns>\n    public static string ToTitleCase(this string @this) => new CultureInfo(\"en-US\").TextInfo.ToTitleCase(Guard.NotNull(@this));\n\n    /// <summary>\n    ///     A string extension method that converts the @this to a title case.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"cultureInfo\">Information describing the culture.</param>\n    /// <returns>@this as a string.</returns>\n    public static string ToTitleCase(this string @this, CultureInfo cultureInfo) => cultureInfo.TextInfo.ToTitleCase(Guard.NotNull(@this));\n\n    /// <summary>\n    ///     A string extension method that truncates.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"maxLength\">The maximum length.</param>\n    /// <returns>A string.</returns>\n    public static string Truncate(this string @this, int maxLength) => Guard.NotNull(@this).Truncate(maxLength, \"...\");\n\n    /// <summary>\n    ///     A string extension method that truncates.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"maxLength\">The maximum length.</param>\n    /// <param name=\"suffix\">The suffix.</param>\n    /// <returns>A string.</returns>\n    public static string Truncate(this string @this, int maxLength, string suffix)\n    {\n        if (Guard.NotNull(@this).Length <= maxLength)\n        {\n            return @this;\n        }\n        return @this.Substring(0, maxLength - suffix.Length) + suffix;\n    }\n\n    /// <summary>\n    /// EqualsIgnoreCase\n    /// </summary>\n    /// <param name=\"s1\">string1</param>\n    /// <param name=\"s2\">string2</param>\n    /// <returns></returns>\n    public static bool EqualsIgnoreCase(this string? s1, string? s2)\n        => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase);\n\n    #endregion string\n\n    #region StringBuilder\n\n    /// <summary>A StringBuilder extension method that substrings.</summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"startIndex\">The start index.</param>\n    /// <returns>A string.</returns>\n    public static string Substring(this StringBuilder @this, int startIndex)\n    {\n        return @this.ToString(startIndex, @this.Length - startIndex);\n    }\n\n    /// <summary>A StringBuilder extension method that substrings.</summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"startIndex\">The start index.</param>\n    /// <param name=\"length\">The length.</param>\n    /// <returns>A string.</returns>\n    public static string Substring(this StringBuilder @this, int startIndex, int length)\n    {\n        return @this.ToString(startIndex, length);\n    }\n\n    /// <summary>A StringBuilder extension method that appends a join.</summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"separator\">The separator.</param>\n    /// <param name=\"values\">The values.</param>\n    public static StringBuilder AppendJoin<T>(this StringBuilder @this, string separator, IEnumerable<T> values)\n    {\n        @this.Append(string.Join(separator, values));\n\n        return @this;\n    }\n\n    /// <summary>A StringBuilder extension method that appends a line join.</summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"separator\">The separator.</param>\n    /// <param name=\"values\">The values.</param>\n    public static StringBuilder AppendLineJoin<T>(this StringBuilder @this, string separator, IEnumerable<T> values)\n    {\n        @this.AppendLine(string.Join(separator, values));\n\n        return @this;\n    }\n\n    /// <summary>\n    /// Append text when condition is true\n    /// </summary>\n    /// <param name=\"builder\">StringBuilder</param>\n    /// <param name=\"text\">text to append</param>\n    /// <param name=\"condition\">condition to evaluate</param>\n    /// <returns>StringBuilder</returns>\n    public static StringBuilder AppendIf(this StringBuilder builder, string text, bool condition)\n    {\n        Guard.NotNull(builder, nameof(builder));\n        if (condition)\n        {\n            builder.Append(text);\n        }\n        return builder;\n    }\n\n    /// <summary>\n    /// Append text when condition is true\n    /// </summary>\n    /// <param name=\"builder\">StringBuilder</param>\n    /// <param name=\"textFactory\">factory for getting text to append</param>\n    /// <param name=\"condition\">condition to evaluate</param>\n    /// <returns>StringBuilder</returns>\n    public static StringBuilder AppendIf(this StringBuilder builder, Func<string> textFactory, bool condition)\n    {\n        Guard.NotNull(builder, nameof(builder));\n        if (condition)\n        {\n            builder.Append(textFactory());\n        }\n        return builder;\n    }\n\n    /// <summary>\n    /// Append text when condition is true\n    /// </summary>\n    /// <param name=\"builder\">StringBuilder</param>\n    /// <param name=\"text\">text to append</param>\n    /// <param name=\"condition\">condition to evaluate</param>\n    /// <returns>StringBuilder</returns>\n    public static StringBuilder AppendLineIf(this StringBuilder builder, string text, bool condition)\n    {\n        Guard.NotNull(builder, nameof(builder));\n\n        if (condition)\n        {\n            builder.AppendLine(text);\n        }\n        return builder;\n    }\n\n    /// <summary>\n    /// Append text when condition is true\n    /// </summary>\n    /// <param name=\"builder\">StringBuilder</param>\n    /// <param name=\"textFactory\">text factory to produce text for appending</param>\n    /// <param name=\"condition\">condition to evaluate</param>\n    /// <returns>StringBuilder</returns>\n    public static StringBuilder AppendLineIf(this StringBuilder builder, Func<string> textFactory, bool condition)\n    {\n        Guard.NotNull(builder, nameof(builder));\n\n        if (condition)\n        {\n            builder.AppendLine(textFactory());\n        }\n        return builder;\n    }\n\n    #endregion StringBuilder\n\n    #region TimeSpan\n\n    /// <summary>\n    ///     A TimeSpan extension method that substract the specified TimeSpan to the current DateTime.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The current DateTime with the specified TimeSpan substracted from it.</returns>\n    public static DateTime Ago(this TimeSpan @this) => DateTime.Now.Subtract(@this);\n\n    /// <summary>\n    ///     A TimeSpan extension method that add the specified TimeSpan to the current DateTime.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The current DateTime with the specified TimeSpan added to it.</returns>\n    public static DateTime FromNow(this TimeSpan @this) => DateTime.Now.Add(@this);\n\n    /// <summary>\n    ///     A TimeSpan extension method that substract the specified TimeSpan to the current UTC (Coordinated Universal\n    ///     Time)\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The current UTC (Coordinated Universal Time) with the specified TimeSpan substracted from it.</returns>\n    public static DateTime UtcAgo(this TimeSpan @this) => DateTime.UtcNow.Subtract(@this);\n\n    /// <summary>\n    ///     A TimeSpan extension method that add the specified TimeSpan to the current UTC (Coordinated Universal Time)\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The current UTC (Coordinated Universal Time) with the specified TimeSpan added to it.</returns>\n    public static DateTime UtcFromNow(this TimeSpan @this) => DateTime.UtcNow.Add(@this);\n\n    #endregion TimeSpan\n\n    #region Type\n\n    /// <summary>\n    ///     A Type extension method that creates an instance.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"args\">The arguments.</param>\n    /// <returns>The new instance.</returns>\n    public static T? CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type @this, params object?[]? args) => (T?)Activator.CreateInstance(@this, args);\n\n    /// <summary>\n    /// if a type has empty constructor\n    /// </summary>\n    /// <param name=\"type\">type</param>\n    /// <returns></returns>\n    public static bool HasEmptyConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type type)\n        => Guard.NotNull(type, nameof(type)).GetConstructors(BindingFlags.Instance).Any(c => c.GetParameters().Length == 0);\n\n    public static bool IsNullableType(this Type type)\n    {\n        Guard.NotNull(type, nameof(type));\n        return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);\n    }\n\n    private static readonly ConcurrentDictionary<Type, object?> DefaultValues = new();\n\n    /// <summary>\n    /// get default value by type, default(T)\n    /// </summary>\n    /// <param name=\"type\">type</param>\n    /// <returns>default value</returns>\n    public static object? GetDefaultValue(this Type type)\n    {\n        Guard.NotNull(type, nameof(type));\n        return type.IsValueType && type != typeof(void)\n            ? DefaultValues.GetOrAdd(type, Activator.CreateInstance)\n            : null;\n    }\n\n    /// <summary>\n    /// GetUnderlyingType if nullable else return self\n    /// </summary>\n    /// <param name=\"type\">type</param>\n    /// <returns></returns>\n    public static Type Unwrap(this Type type)\n        => Nullable.GetUnderlyingType(Guard.NotNull(type, nameof(type))) ?? type;\n\n    /// <summary>\n    /// GetUnderlyingType\n    /// </summary>\n    /// <param name=\"type\">type</param>\n    /// <returns></returns>\n    public static Type? GetUnderlyingType(this Type type)\n        => Nullable.GetUnderlyingType(Guard.NotNull(type, nameof(type)));\n\n    #endregion Type\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/CronExtension.cs",
    "content": "﻿using WeihanLi.Common;\nusing WeihanLi.Common.Helpers.Cron;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class CronExtension\n{\n    /// <summary>\n    /// GetNextOccurrence\n    /// </summary>\n    /// <param name=\"expression\">cron expression</param>\n    /// <returns>next occurrence time</returns>\n    public static DateTimeOffset? GetNextOccurrence(this CronExpression? expression)\n    {\n        return expression?.GetNextOccurrence(DateTimeOffset.UtcNow, TimeZoneInfo.Utc);\n    }\n\n    /// <summary>\n    /// GetNextOccurrence\n    /// </summary>\n    /// <param name=\"expression\">cron expression</param>\n    /// <param name=\"timeZoneInfo\">timeZoneInfo</param>\n    /// <returns>next occurrence time</returns>\n    public static DateTimeOffset? GetNextOccurrence(this CronExpression? expression, TimeZoneInfo timeZoneInfo)\n    {\n        return expression?.GetNextOccurrence(DateTimeOffset.UtcNow, timeZoneInfo);\n    }\n\n    /// <summary>\n    /// GetNextOccurrence\n    /// </summary>\n    /// <param name=\"expression\">cron expression</param>\n    /// <param name=\"period\">next period</param>\n    /// <returns>next occurrence times</returns>\n    public static IEnumerable<DateTimeOffset> GetNextOccurrences(this CronExpression expression, TimeSpan period)\n    {\n        Guard.NotNull(expression, nameof(expression));\n        var utcNow = DateTime.UtcNow;\n        return expression.GetOccurrences(utcNow, utcNow.Add(period), TimeZoneInfo.Utc);\n    }\n\n    /// <summary>\n    /// GetNextOccurrence\n    /// </summary>\n    /// <param name=\"expression\">cron expression</param>\n    /// <param name=\"period\">next period</param>\n    /// <param name=\"timeZoneInfo\">timeZoneInfo</param>\n    /// <returns>next occurrence times</returns>\n    public static IEnumerable<DateTimeOffset> GetNextOccurrences(this CronExpression expression, TimeSpan period, TimeZoneInfo timeZoneInfo)\n    {\n        var utcNow = DateTime.UtcNow;\n        return expression.GetOccurrences(utcNow, utcNow.Add(period), timeZoneInfo);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/DataExtension.cs",
    "content": "﻿using System.Collections;\nusing System.ComponentModel;\nusing System.Data;\nusing System.Data.Common;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Dynamic;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Logging;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static partial class DataExtension\n{\n    private sealed class DbParameterReadOnlyCollection(DbParameterCollection parameterCollection) : IReadOnlyCollection<DbParameter>\n    {\n        private readonly DbParameterCollection _paramCollection = parameterCollection;\n\n        public int Count => _paramCollection.Count;\n\n        public IEnumerator<DbParameter> GetEnumerator()\n        {\n            return (IEnumerator<DbParameter>)_paramCollection.GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n    }\n\n    private static DbParameterReadOnlyCollection GetReadOnlyCollection(this DbParameterCollection collection) => new(collection);\n\n    public static Func<DbCommand, string> CommandLogFormatterFunc = command =>\n           $\"DbCommand log: CommandText:{command.CommandText},CommandType:{command.CommandType},Parameters:{command.Parameters.GetReadOnlyCollection().Select(p => $\"{p.ParameterName}={p.Value}\").StringJoin(\",\")},CommandTimeout:{command.CommandTimeout}s\";\n\n    public static Action<string>? CommandLogAction = log => Debug.WriteLine(log);\n\n    #region DataTable\n\n    public static DataTable ToDataTable<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this IEnumerable<T> entities)\n    {\n        Guard.NotNull(entities);\n        var properties = CacheUtil.GetTypeProperties(typeof(T))\n            .Where(_ => _.CanRead)\n            .ToArray();\n        var dataTable = new DataTable();\n        dataTable.Columns.AddRange(properties.Select(p => new DataColumn(p.Name, p.PropertyType)).ToArray());\n        foreach (var item in entities)\n        {\n            var row = dataTable.NewRow();\n            foreach (var property in properties)\n            {\n                row[property.Name] = property.GetValueGetter<T>()?.Invoke(item);\n            }\n            dataTable.Rows.Add(row);\n        }\n        return dataTable;\n    }\n\n    private static object? GetValueFromDbValue(this object? obj)\n    {\n        if (obj == null || obj == DBNull.Value)\n        {\n            return null;\n        }\n        return obj;\n    }\n\n    /// <summary>\n    ///     Enumerates to entities in this collection.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as an IEnumerable&lt;T&gt;</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static IEnumerable<T> ToEntities<T>(this DataTable @this)\n    {\n        if (@this.Columns.Count > 0)\n        {\n            if (typeof(T).IsBasicType())\n            {\n                foreach (DataRow row in @this.Rows)\n                {\n                    yield return row[0].To<T>();\n                }\n            }\n            else\n            {\n                foreach (DataRow dr in @this.Rows)\n                {\n                    yield return dr.ToEntity<T>();\n                }\n            }\n        }\n    }\n\n    /// <summary>\n    ///     Enumerates to expando objects in this collection.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as an IEnumerable&lt;dynamic&gt;</returns>\n    public static IEnumerable<dynamic> ToExpandoObjects(this DataTable @this)\n    {\n        foreach (DataRow dr in @this.Rows)\n        {\n            dynamic entity = new ExpandoObject();\n            var expandoDict = (IDictionary<string, object>)entity;\n\n            foreach (DataColumn column in @this.Columns)\n            {\n                expandoDict.Add(column.ColumnName, dr[column]);\n            }\n\n            yield return entity;\n        }\n    }\n\n    /// <summary>\n    ///     Enumerates index column to list in this collection.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"dataTable\">The dataTable to act on.</param>\n    /// <param name=\"index\">column index</param>\n    /// <returns>@this as an IEnumerable&lt;T&gt;</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static IEnumerable<T?> ColumnToList<T>(this DataTable dataTable, int index)\n    {\n        Guard.NotNull(dataTable, nameof(dataTable));\n        if (dataTable.Rows.Count > index)\n        {\n            foreach (DataRow row in dataTable.Rows)\n            {\n                yield return row[index].ToOrDefault<T>();\n            }\n        }\n    }\n\n    #endregion DataTable\n\n    #region DataRow\n\n    /// <summary>\n    ///     A DataRow extension method that converts the @this to the entities.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"dr\">The @this to act on.</param>\n    /// <returns>@this as a T.</returns>\n    public static T ToEntity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this DataRow dr)\n    {\n        var type = typeof(T);\n        var properties = CacheUtil.GetTypeProperties(type).Where(p => p.CanWrite).ToArray();\n\n        var entity = NewFuncHelper<T>.Instance()!;\n\n        if (type.IsValueType)\n        {\n            object obj = entity;\n            foreach (var property in properties)\n            {\n                if (dr.Table.Columns.Contains(property.Name))\n                {\n                    property.GetValueSetter()?.Invoke(obj, dr[property.Name].GetValueFromDbValue());\n                }\n            }\n            entity = (T)obj;\n        }\n        else\n        {\n            foreach (var property in properties)\n            {\n                if (dr.Table.Columns.Contains(property.Name))\n                {\n                    property.GetValueSetter()?.Invoke(entity, dr[property.Name].GetValueFromDbValue());\n                }\n            }\n        }\n\n        return entity;\n    }\n\n    /// <summary>A DataRow extension method that converts the @this to an expando object.</summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as a dynamic.</returns>\n    public static dynamic ToExpandoObject(this DataRow @this)\n    {\n        dynamic entity = new ExpandoObject();\n        var expandoDict = (IDictionary<string, object>)entity;\n\n        foreach (DataColumn column in @this.Table.Columns)\n        {\n            expandoDict.Add(column.ColumnName, @this[column]);\n        }\n\n        return expandoDict;\n    }\n\n    #endregion DataRow\n\n    #region IDataReader\n\n    /// <summary>\n    ///     An IDataReader extension method that converts the @this to a data table.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as a DataTable.</returns>\n    [RequiresUnreferencedCode(\"Members from types used in the expression column to be trimmed if not referenced directly.\")]\n    public static DataTable ToDataTable(this IDataReader @this)\n    {\n        var dt = new DataTable();\n        dt.Load(@this);\n        return dt;\n    }\n\n    /// <summary>\n    ///     Enumerates to entities in this collection.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as an IEnumerable&lt;T&gt;</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static IEnumerable<T> ToEntities<T>(this IDataReader @this)\n    {\n        var type = typeof(T);\n        if (type.IsBasicType())\n        {\n            while (@this.Read())\n            {\n                yield return @this[0].To<T>();\n            }\n        }\n        else\n        {\n            while (@this.Read())\n            {\n                yield return @this.ToEntity<T>(true)!;\n            }\n        }\n    }\n\n    /// <summary>\n    ///     An IDataReader extension method that converts the @this to an entity.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"hadRead\">whether the DataReader had read</param>\n    /// <returns>@this as a T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T? ToEntity<T>(this IDataReader @this, bool hadRead = false)\n    {\n        if (!hadRead)\n        {\n            hadRead = @this.Read();\n        }\n\n        if (hadRead && @this.FieldCount > 0)\n        {\n            var type = typeof(T);\n            if (type.IsBasicType())\n            {\n                return @this[0].ToOrDefault<T>();\n            }\n\n            var properties = CacheUtil.GetTypeProperties(type).Where(p => p.CanWrite).ToArray();\n\n            var entity = NewFuncHelper<T>.Instance()!;\n\n            var dic = Enumerable.Range(0, @this.FieldCount)\n                .ToDictionary(_ => @this.GetName(_).ToUpper(), _ => @this[_].GetValueFromDbValue());\n            try\n            {\n                if (type.IsValueType)\n                {\n                    var obj = (object)entity;\n                    foreach (var property in properties)\n                    {\n                        if (dic.ContainsKey(property.Name.ToUpper()))\n                        {\n                            property.GetValueSetter()?.Invoke(obj, dic[property.Name.ToUpper()]);\n                        }\n                    }\n                    entity = (T)obj;\n                }\n                else\n                {\n                    foreach (var property in properties)\n                    {\n                        if (dic.ContainsKey(property.Name.ToUpper()))\n                        {\n                            property.GetValueSetter()?.Invoke(entity, dic[property.Name.ToUpper()]);\n                        }\n                    }\n                }\n\n                return entity;\n            }\n            catch (Exception e)\n            {\n                Common.Helpers.LogHelper.GetLogger(typeof(DataExtension)).Error(e);\n                throw;\n            }\n        }\n\n        return default;\n    }\n\n    /// <summary>\n    ///     An IDataReader extension method that converts the @this to an expando object.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"hadRead\">whether the DataReader had read</param>\n    /// <returns>@this as a dynamic.</returns>\n    public static dynamic ToExpandoObject(this IDataReader @this, bool hadRead = false)\n    {\n        dynamic entity = new ExpandoObject();\n        if (!hadRead)\n        {\n            hadRead = @this.Read();\n        }\n\n        if (hadRead && @this.FieldCount > 0)\n        {\n            var expandoDict = (IDictionary<string, object>)entity;\n            var columnNames = Enumerable.Range(0, @this.FieldCount)\n                .Select(x => new KeyValuePair<int, string>(x, @this.GetName(x)))\n                .ToDictionary(pair => pair.Key);\n\n            Enumerable.Range(0, @this.FieldCount)\n                .ToList()\n                .ForEach(x => expandoDict.Add(columnNames[x].Value, @this[x]));\n        }\n\n        return entity;\n    }\n\n    /// <summary>\n    ///     Enumerates to expando objects in this collection.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as an IEnumerable&lt;dynamic&gt;</returns>\n    public static IEnumerable<dynamic> ToExpandoObjects(this IDataReader @this)\n    {\n        var columnNames = Enumerable.Range(0, @this.FieldCount)\n            .Select(x => new KeyValuePair<int, string>(x, @this.GetName(x)))\n            .ToDictionary(pair => pair.Key);\n\n        while (@this.Read())\n        {\n            dynamic entity = new ExpandoObject();\n            if (@this.FieldCount > 0)\n            {\n                var expandoDict = (IDictionary<string, object>)entity;\n\n                Enumerable.Range(0, @this.FieldCount)\n                    .ToList()\n                    .ForEach(x => expandoDict.Add(columnNames[x].Value, @this[x]));\n            }\n\n            yield return entity;\n        }\n    }\n\n    #endregion IDataReader\n\n    #region IDbConnection\n\n    /// <summary>\n    ///     An IDbConnection extension method that ensures that open.\n    /// </summary>\n    /// <param name=\"connection\">The connection to act on.</param>\n    public static void EnsureOpen(this IDbConnection connection)\n    {\n        if (connection.State == ConnectionState.Closed)\n        {\n            connection.Open();\n        }\n    }\n\n    /// <summary>A DbConnection extension method that queries if a connection is open.</summary>\n    /// <param name=\"connection\">The connection to act on.</param>\n    /// <returns>true if a connection is open, false if not.</returns>\n    public static bool IsConnectionOpen(this IDbConnection connection)\n    {\n        return connection.State == ConnectionState.Open;\n    }\n\n    #endregion IDbConnection\n\n    #region DbConnection\n\n    /// <summary>\n    ///     An DbConnection extension method that ensures that open.\n    /// </summary>\n    /// <param name=\"conn\">The @this to act on.</param>\n    public static async Task EnsureOpenAsync(this DbConnection conn)\n    {\n        if (conn.State == ConnectionState.Closed)\n        {\n            await conn.OpenAsync().ConfigureAwait(false);\n        }\n    }\n\n    #endregion DbConnection\n\n    #region DbCommand\n\n    /// <summary>\n    ///     A DbCommand extension method that executes the expando object operation.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>A dynamic.</returns>\n    public static dynamic ExecuteExpandoObject(this DbCommand @this)\n    {\n        using IDataReader reader = @this.ExecuteReader();\n        reader.Read();\n        return reader.ToExpandoObject();\n    }\n\n    /// <summary>\n    ///     Enumerates execute expando objects in this collection.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>\n    ///     An enumerator that allows foreach to be used to process execute expando objects in this collection.\n    /// </returns>\n    public static IEnumerable<dynamic> ExecuteExpandoObjects(this DbCommand @this)\n    {\n        using IDataReader reader = @this.ExecuteReader();\n        return reader.ToExpandoObjects();\n    }\n\n    /// <summary>\n    /// ExecuteDataTableTo\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"this\">db command</param>\n    /// <param name=\"func\">function</param>\n    /// <returns></returns>\n    public static T ExecuteDataTable<T>(this DbCommand @this, Func<DataTable, T> func) => func(@this.ExecuteDataTable());\n\n    /// <summary>\n    /// ExecuteDataTableToAsync\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"this\">db command</param>\n    /// <param name=\"func\">function</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns></returns>\n    public static async Task<T> ExecuteDataTableAsync<T>(this DbCommand @this, Func<DataTable, Task<T>> func, CancellationToken cancellationToken = default)\n    {\n        var dataTable = await @this.ExecuteDataTableAsync(cancellationToken);\n        var result = await func(dataTable);\n        return result;\n    }\n\n    /// <summary>\n    ///     A DbCommand extension method that executes the scalar to operation.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>A T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T ExecuteScalarTo<T>(this DbCommand @this) => @this.ExecuteScalar().To<T>();\n\n    /// <summary>\n    ///     A DbCommand extension method that executes the scalar to operation.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"cancellationToken\"></param>\n    /// <returns>A T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static async Task<T> ExecuteScalarToAsync<T>(this DbCommand @this, CancellationToken cancellationToken = default) => (await @this.ExecuteScalarAsync(cancellationToken)).To<T>();\n\n    /// <summary>\n    ///     A DbCommand extension method that executes the scalar to operation.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>A T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T? ExecuteScalarToOrDefault<T>(this DbCommand @this) => @this.ExecuteScalar().ToOrDefault<T>();\n\n    /// <summary>\n    ///     A DbCommand extension method that executes the scalar to operation.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"cancellationToken\"></param>\n    /// <returns>A T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static async Task<T?> ExecuteScalarToOrDefaultAsync<T>(this DbCommand @this, CancellationToken cancellationToken = default) => (await @this.ExecuteScalarAsync(cancellationToken)).ToOrDefault<T>();\n\n    /// <summary>\n    ///     A DbCommand extension method that executes the scalar to or default operation.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"func\">The default value factory.</param>\n    /// <returns>A T.</returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T? ExecuteScalarTo<T>(this DbCommand @this, Func<object?, T> func)\n    {\n        return func(@this.ExecuteScalar());\n    }\n\n    private static DbCommand GetDbCommand(this DbConnection conn, string cmdText, CommandType commandType = CommandType.Text, object? paramInfo = null, DbParameter[]? parameters = null, DbTransaction? transaction = null, int commandTimeout = 60)\n    {\n        conn.EnsureOpen();\n        var command = conn.CreateCommand();\n\n        command.CommandText = cmdText;\n        command.CommandType = commandType;\n        command.Transaction = transaction;\n        command.CommandTimeout = commandTimeout;\n\n        if (parameters != null)\n        {\n            command.Parameters.AddRange(parameters);\n        }\n        command.AttachDbParameters(paramInfo);\n        //\n        CommandLogAction?.Invoke(CommandLogFormatterFunc(command));\n\n        return command;\n    }\n\n    #endregion DbCommand\n\n    #region DbParameter\n\n    public static bool ContainsParam(this DbParameterCollection @this, string paramName)\n    {\n        var originName = GetParameterName(paramName);\n        return @this.Contains(originName)\n               || @this.Contains(\"@\" + originName)\n               || @this.Contains(\"?\" + originName);\n    }\n\n    public static void AttachDbParameters(this DbCommand command, object? paramInfo)\n    {\n        if (paramInfo != null)\n        {\n            var paramType = paramInfo.GetType();\n            if (paramInfo is not IDictionary<string, object?> parameters)\n            {\n                if (paramType.IsValueTuple()) // Tuple\n                {\n                    parameters = CacheUtil.GetTypeFields(paramType)\n                        .ToDictionary(f => f.Name, f => f?.GetValue(paramInfo));\n                }\n                else // get properties\n                {\n                    parameters = CacheUtil.GetTypeProperties(paramType)\n                        .ToDictionary(x => x.Name, x => x.GetValueGetter()?.Invoke(paramInfo));\n                }\n            }\n            //\n            foreach (var parameter in parameters)\n            {\n                var param = command.CreateParameter();\n                param.ParameterName = GetParameterName(parameter.Key);\n                param.Value = parameter.Value ?? DBNull.Value;\n                param.DbType = parameter.Value?.GetType().ToDbType() ?? DbType.String;\n                command.Parameters.Add(param);\n            }\n        }\n    }\n\n    /// <summary>\n    /// 获取参数名称\n    /// </summary>\n    /// <param name=\"originName\">原参数名</param>\n    /// <returns>格式化后的参数名</returns>\n    private static string GetParameterName(string originName)\n    {\n        if (!string.IsNullOrEmpty(originName))\n        {\n            switch (originName[0])\n            {\n                case '@':\n                case ':':\n                case '?':\n                    return originName[1..];\n            }\n        }\n        return originName;\n    }\n\n    public static readonly Dictionary<Type, DbType> TypeMap = new()\n    {\n        [typeof(byte)] = DbType.Byte,\n        [typeof(sbyte)] = DbType.SByte,\n        [typeof(short)] = DbType.Int16,\n        [typeof(ushort)] = DbType.UInt16,\n        [typeof(int)] = DbType.Int32,\n        [typeof(uint)] = DbType.UInt32,\n        [typeof(long)] = DbType.Int64,\n        [typeof(ulong)] = DbType.UInt64,\n        [typeof(float)] = DbType.Single,\n        [typeof(double)] = DbType.Double,\n        [typeof(decimal)] = DbType.Decimal,\n        [typeof(bool)] = DbType.Boolean,\n        [typeof(string)] = DbType.String,\n        [typeof(char)] = DbType.StringFixedLength,\n        [typeof(Guid)] = DbType.Guid,\n        [typeof(DateTime)] = DbType.DateTime2,\n        [typeof(DateTimeOffset)] = DbType.DateTimeOffset,\n        [typeof(TimeSpan)] = DbType.Time,\n        [typeof(byte[])] = DbType.Binary,\n        [typeof(object)] = DbType.Object,\n#if NET\n        [typeof(DateOnly)] = DbType.Date,\n        [typeof(TimeOnly)] = DbType.Time,\n#endif\n    };\n\n    public static DbType ToDbType(this Type type)\n    {\n        if (type.IsEnum && !TypeMap.ContainsKey(type))\n        {\n            type = Enum.GetUnderlyingType(type);\n        }\n        if (TypeMap.TryGetValue(type.Unwrap(), out var dbType))\n        {\n            return dbType;\n        }\n        if (type.FullName == \"System.Data.Linq.Binary\")\n        {\n            return DbType.Binary;\n        }\n        return DbType.Object;\n    }\n\n    #endregion DbParameter\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/DbCommandExtension.generated.cs",
    "content": "﻿using System.Data;\nusing System.Data.Common;\n\nnamespace WeihanLi.Extensions;\n\n#nullable enable\n\npublic static partial class DataExtension\n{\n          \n    public static IEnumerable<dynamic> Select(this DbCommand command) \n    {\n        using (var reader = command.ExecuteReader())\n        {\n            return reader.ToExpandoObjects();\n        }\n    }\n\n    public static async Task<IEnumerable<dynamic>> SelectAsync(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))\n        {\n            var list = new List<dynamic>();\n            while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))\n            {\n                list.Add(reader.ToExpandoObject(true));\n            }\n            return list;\n        }\n    }\n\n    public static IEnumerable<T> Select<T>(this DbCommand command) \n    {\n        using (var reader = command.ExecuteReader())\n        {\n            var list = new List<T>();\n            while (reader.Read())\n            {\n                list.Add(reader.ToEntity<T>(true)!);\n            }\n            return list;\n        }\n    }\n\n    public static async Task<IEnumerable<T>> SelectAsync<T>(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken))\n        {\n            var list = new List<T>();\n            while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))\n            {\n                list.Add(reader.ToEntity<T>(true)!);\n            }\n            return list;\n        }\n    }\n\n      \n    public static dynamic Fetch(this DbCommand command)\n    {\n        using (var reader = command.ExecuteReader())\n        {\n            return reader.ToExpandoObject();\n        }\n    }\n\n    public static async Task<dynamic> FetchAsync(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))\n        {\n            await reader.ReadAsync().ConfigureAwait(false);\n            return reader.ToExpandoObject(true);\n        }\n    }\n\n    public static T? Fetch<T>(this DbCommand command)\n    {\n        using (var reader = command.ExecuteReader())\n        {\n            return reader.ToEntity<T>();\n        }\n    }\n\n    public static async Task<T?> FetchAsync<T>(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))\n        {\n            return reader.ToEntity<T>();\n        }\n    }\n\n      \n    public static DataTable ExecuteDataTable(this DbCommand command)\n    {\n        using (var reader = command.ExecuteReader())\n        {\n            return reader.ToDataTable();\n        }\n    }\n\n    public static async Task<DataTable> ExecuteDataTableAsync(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))\n        {\n            return reader.ToDataTable();\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/DbCommandExtension.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n<#@ output extension=\".generated.cs\" #>\nusing System.Data;\nusing System.Data.Common;\n\nnamespace WeihanLi.Extensions;\n\n#nullable enable\n\npublic static partial class DataExtension\n{\n    <#\nvar commandNames = new string[]{\n    \"Select\",\n    };\nforeach(var cmd in commandNames)\n{ \n#>      \n    public static IEnumerable<dynamic> <#=            cmd #>(this DbCommand command) \n    {\n        using (var reader = command.ExecuteReader())\n        {\n            return reader.ToExpandoObjects();\n        }\n    }\n\n    public static async Task<IEnumerable<dynamic>> <#=            cmd #>Async(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))\n        {\n            var list = new List<dynamic>();\n            while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))\n            {\n                list.Add(reader.ToExpandoObject(true));\n            }\n            return list;\n        }\n    }\n\n    public static IEnumerable<T> <#=            cmd #><T>(this DbCommand command) \n    {\n        using (var reader = command.ExecuteReader())\n        {\n            var list = new List<T>();\n            while (reader.Read())\n            {\n                list.Add(reader.ToEntity<T>(true)!);\n            }\n            return list;\n        }\n    }\n\n    public static async Task<IEnumerable<T>> <#=            cmd #>Async<T>(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken))\n        {\n            var list = new List<T>();\n            while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))\n            {\n                list.Add(reader.ToEntity<T>(true)!);\n            }\n            return list;\n        }\n    }\n<#    }#>\n\n<#\nvar commandNames1 = new string[]{\n    \"Fetch\"\n    };\nforeach(var cmd in commandNames1)\n{ \n#>      \n    public static dynamic <#=            cmd #>(this DbCommand command)\n    {\n        using (var reader = command.ExecuteReader())\n        {\n            return reader.ToExpandoObject();\n        }\n    }\n\n    public static async Task<dynamic> <#=            cmd #>Async(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))\n        {\n            await reader.ReadAsync().ConfigureAwait(false);\n            return reader.ToExpandoObject(true);\n        }\n    }\n\n    public static T? <#=            cmd #><T>(this DbCommand command)\n    {\n        using (var reader = command.ExecuteReader())\n        {\n            return reader.ToEntity<T>();\n        }\n    }\n\n    public static async Task<T?> <#=            cmd #>Async<T>(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))\n        {\n            return reader.ToEntity<T>();\n        }\n    }\n<#    }#>\n\n<#\nvar commandNames2 = new string[]{\n    \"ExecuteDataTable\"\n    };\nforeach(var cmd in commandNames2)\n{ \n#>      \n    public static DataTable <#=            cmd #>(this DbCommand command)\n    {\n        using (var reader = command.ExecuteReader())\n        {\n            return reader.ToDataTable();\n        }\n    }\n\n    public static async Task<DataTable> <#=            cmd #>Async(this DbCommand command, CancellationToken cancellationToken = default)\n    {\n        using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))\n        {\n            return reader.ToDataTable();\n        }\n    }\n<#    }#>\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/DbConnectionExtension.generated.cs",
    "content": "﻿using System.Data;\nusing System.Data.Common;\n\nnamespace WeihanLi.Extensions;\n#nullable enable\npublic static partial class DataExtension\n{\n    public static int Execute(this DbConnection conn, string cmdText, int commandTimeout = 60) => conn.Execute(cmdText, null, commandTimeout);\n\n    public static int Execute(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60) => conn.Execute(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout);\n\n    public static int Execute(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.Execute(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static int Execute(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout = 60)=> conn.Execute(cmdText, commandType, paramInfo, parameters, null, commandTimeout);\n\n    public static int Execute(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60)\n    {\n      conn.EnsureOpen();\n      try\n      {\n        using var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout);\n        return command.ExecuteNonQuery();\n      }\n      finally\n      {\n        conn.Close();\n      }\n    }\n\n    public static Task<int> ExecuteAsync(this DbConnection conn, string cmdText, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.ExecuteAsync(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<int> ExecuteAsync(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.ExecuteAsync(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<int> ExecuteAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.ExecuteAsync(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<int> ExecuteAsync(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.ExecuteAsync(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static async Task<int> ExecuteAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{        \nusing (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.ExecuteNonQueryAsync(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\npublic static object? ExecuteScalar(this DbConnection conn, string cmdText, int commandTimeout = 60) => conn.ExecuteScalar(cmdText, null, commandTimeout);\n\n    public static object? ExecuteScalar(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60) => conn.ExecuteScalar(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout);\n\n    public static object? ExecuteScalar(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.ExecuteScalar(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static object? ExecuteScalar(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout = 60)=> conn.ExecuteScalar(cmdText, commandType, paramInfo, parameters, null, commandTimeout);\n\n    public static object? ExecuteScalar(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60)\n    {\n      conn.EnsureOpen();\n      try\n      {\n        using var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout);\n        return command.ExecuteScalar();\n      }\n      finally\n      {\n        conn.Close();\n      }\n    }\n\n    public static Task<object?> ExecuteScalarAsync(this DbConnection conn, string cmdText, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.ExecuteScalarAsync(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<object?> ExecuteScalarAsync(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.ExecuteScalarAsync(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<object?> ExecuteScalarAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.ExecuteScalarAsync(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<object?> ExecuteScalarAsync(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.ExecuteScalarAsync(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static async Task<object?> ExecuteScalarAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{        \nusing (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.ExecuteScalarAsync(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\npublic static dynamic Fetch(this DbConnection conn, string cmdText, int commandTimeout = 60) => conn.Fetch(cmdText, null, commandTimeout);\n\n    public static dynamic Fetch(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60) => conn.Fetch(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout);\n\n    public static dynamic Fetch(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.Fetch(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static dynamic Fetch(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout = 60)=> conn.Fetch(cmdText, commandType, paramInfo, parameters, null, commandTimeout);\n\n    public static dynamic Fetch(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60)\n    {\n      conn.EnsureOpen();\n      try\n      {\n        using var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout);\n        return command.Fetch();\n      }\n      finally\n      {\n        conn.Close();\n      }\n    }\n\n    public static Task<dynamic> FetchAsync(this DbConnection conn, string cmdText, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.FetchAsync(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<dynamic> FetchAsync(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.FetchAsync(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<dynamic> FetchAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.FetchAsync(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<dynamic> FetchAsync(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.FetchAsync(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static async Task<dynamic> FetchAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{        \nusing (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.FetchAsync(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\npublic static DataTable ExecuteDataTable(this DbConnection conn, string cmdText, int commandTimeout = 60) => conn.ExecuteDataTable(cmdText, null, commandTimeout);\n\n    public static DataTable ExecuteDataTable(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60) => conn.ExecuteDataTable(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout);\n\n    public static DataTable ExecuteDataTable(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.ExecuteDataTable(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static DataTable ExecuteDataTable(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout = 60)=> conn.ExecuteDataTable(cmdText, commandType, paramInfo, parameters, null, commandTimeout);\n\n    public static DataTable ExecuteDataTable(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60)\n    {\n      conn.EnsureOpen();\n      try\n      {\n        using var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout);\n        return command.ExecuteDataTable();\n      }\n      finally\n      {\n        conn.Close();\n      }\n    }\n\n    public static Task<DataTable> ExecuteDataTableAsync(this DbConnection conn, string cmdText, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.ExecuteDataTableAsync(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<DataTable> ExecuteDataTableAsync(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.ExecuteDataTableAsync(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<DataTable> ExecuteDataTableAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.ExecuteDataTableAsync(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<DataTable> ExecuteDataTableAsync(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.ExecuteDataTableAsync(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static async Task<DataTable> ExecuteDataTableAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{        \nusing (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.ExecuteDataTableAsync(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n   public static T? Fetch<T>(this DbConnection conn, string cmdText, int commandTimeout = 60)  => conn.Fetch<T>(cmdText, CommandType.Text, null, commandTimeout);\n        \n    public static T? Fetch<T>(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60)  => conn.Fetch<T>(cmdText, CommandType.Text, paramInfo,null, null, commandTimeout);\n\n    public static T? Fetch<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60)  => conn.Fetch<T>(cmdText, commandType, paramInfo, null, null, commandTimeout);\n\n    public static T? Fetch<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.Fetch<T>(cmdText, CommandType.Text, paramInfo , parameters, null);\n\n    public static T? Fetch<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60) \n    {\n        conn.EnsureOpen();\n        try\n        {\n            using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n            {\n                return command.Fetch<T>();\n            }\n        }\n        finally\n        {\n            conn.Close();\n        }\n    }\n\n    public static Task<T?> FetchAsync<T>(this DbConnection conn, string cmdText, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.FetchAsync<T>(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<T?> FetchAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.FetchAsync<T>(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<T?> FetchAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.FetchAsync<T>(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<T?> FetchAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.FetchAsync<T>(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static Task<T?> FetchAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout=60, CancellationToken cancellationToken = default) => conn.FetchAsync<T>(cmdText, commandType, paramInfo, parameters, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static async Task<T?> FetchAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default) \n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.FetchAsync<T>(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n\n   public static IEnumerable<T> Select<T>(this DbConnection conn, string cmdText, int commandTimeout = 60)  => conn.Select<T>(cmdText, CommandType.Text, null, commandTimeout);\n        \n    public static IEnumerable<T> Select<T>(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60)  => conn.Select<T>(cmdText, CommandType.Text, paramInfo,null, null, commandTimeout);\n\n    public static IEnumerable<T> Select<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60)  => conn.Select<T>(cmdText, commandType, paramInfo, null, null, commandTimeout);\n\n    public static IEnumerable<T> Select<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.Select<T>(cmdText, CommandType.Text, paramInfo , parameters, null);\n\n    public static IEnumerable<T> Select<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60) \n    {\n        conn.EnsureOpen();\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return command.Select<T>();\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<IEnumerable<T>> SelectAsync<T>(this DbConnection conn, string cmdText, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.SelectAsync<T>(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<IEnumerable<T>> SelectAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.SelectAsync<T>(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<IEnumerable<T>> SelectAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.SelectAsync<T>(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<IEnumerable<T>> SelectAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.SelectAsync<T>(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static Task<IEnumerable<T>> SelectAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout=60, CancellationToken cancellationToken = default) => conn.SelectAsync<T>(cmdText, commandType, paramInfo, parameters, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static async Task<IEnumerable<T>> SelectAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default) \n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.SelectAsync<T>(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n   public static IEnumerable<dynamic> Select(this DbConnection conn, string cmdText, int commandTimeout = 60)  => conn.Select(cmdText, CommandType.Text, null, commandTimeout);\n        \n    public static IEnumerable<dynamic> Select(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60)  => conn.Select(cmdText, CommandType.Text, paramInfo,null, null, commandTimeout);\n\n    public static IEnumerable<dynamic> Select(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60)  => conn.Select(cmdText, commandType, paramInfo, null, null, commandTimeout);\n\n    public static IEnumerable<dynamic> Select(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.Select(cmdText, CommandType.Text, paramInfo , parameters, null);\n\n    public static IEnumerable<dynamic> Select(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60) \n    {\n        conn.EnsureOpen();\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return command.Select();\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<IEnumerable<dynamic>> SelectAsync(this DbConnection conn, string cmdText, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.SelectAsync(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<IEnumerable<dynamic>> SelectAsync(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.SelectAsync(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<IEnumerable<dynamic>> SelectAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.SelectAsync(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<IEnumerable<dynamic>> SelectAsync(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.SelectAsync(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static Task<IEnumerable<dynamic>> SelectAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout=60, CancellationToken cancellationToken = default) => conn.SelectAsync(cmdText, commandType, paramInfo, parameters, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static async Task<IEnumerable<dynamic>> SelectAsync(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default) \n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.SelectAsync(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n\n      public static T ExecuteDataTable<T>(this DbConnection conn, string cmdText, Func<DataTable, T> func)  => conn.ExecuteDataTable<T>(cmdText,null, null, func);\n        \n    public static T ExecuteDataTable<T>(this DbConnection conn, string cmdText, object? paramInfo, Func<DataTable, T> func)  => conn.ExecuteDataTable<T>(cmdText, CommandType.Text, paramInfo,null, null, func);\n\n    public static T ExecuteDataTable<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, Func<DataTable, T> func)  => conn.ExecuteDataTable<T>(cmdText, commandType, paramInfo, null, null, func);\n\n    public static T ExecuteDataTable<T>(this DbConnection conn, string cmdText, object? paramInfo, DbParameter[]? parameters, Func<DataTable, T> func) => conn.ExecuteDataTable<T>(cmdText, CommandType.Text, paramInfo , parameters, null, func);\n\n    public static T ExecuteDataTable<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, DbTransaction? transaction, Func<DataTable, T> func, int commandTimeout = 60) \n    {\n        conn.EnsureOpen();\ntry{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return command.ExecuteDataTable(func);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<T> ExecuteDataTableAsync<T>(this DbConnection conn, string cmdText,Func<DataTable, T> func, CancellationToken cancellationToken = default)  => conn.ExecuteDataTableAsync<T>(cmdText, null, func, cancellationToken: cancellationToken);\n\n    public static Task<T> ExecuteDataTableAsync<T>(this DbConnection conn, string cmdText, object? paramInfo,Func<DataTable, T> func, CancellationToken cancellationToken = default)  => conn.ExecuteDataTableAsync<T>(cmdText, CommandType.Text, paramInfo, null, null, func, cancellationToken: cancellationToken);\n\n    public static Task<T> ExecuteDataTableAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo,Func<DataTable, T> func, CancellationToken cancellationToken = default)  => conn.ExecuteDataTableAsync<T>(cmdText, commandType, paramInfo, null, null, func, cancellationToken: cancellationToken);\n\n    public static Task<T> ExecuteDataTableAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, DbParameter[]? parameters,Func<DataTable, T> func, CancellationToken cancellationToken = default) => conn.ExecuteDataTableAsync<T>(cmdText, CommandType.Text, paramInfo, parameters, null, func, cancellationToken: cancellationToken);\n\n    public static Task<T> ExecuteDataTableAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,Func<DataTable, T> func, CancellationToken cancellationToken = default) => conn.ExecuteDataTableAsync<T>(cmdText, commandType, paramInfo, parameters, func, cancellationToken: cancellationToken);\n\n    public static async Task<T> ExecuteDataTableAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction,Func<DataTable, T> func, int commandTimeout = 60, CancellationToken cancellationToken = default) \n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return func(await command.ExecuteDataTableAsync(cancellationToken));\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n      public static T ExecuteScalarTo<T>(this DbConnection conn, string cmdText) => conn.ExecuteScalarTo<T>(cmdText,null, null);\n        \n    public static T ExecuteScalarTo<T>(this DbConnection conn, string cmdText, object? paramInfo) => conn.ExecuteScalarTo<T>(cmdText, CommandType.Text, paramInfo,null, null);\n\n    public static T ExecuteScalarTo<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo) => conn.ExecuteScalarTo<T>(cmdText, commandType, paramInfo, null, null);\n\n    public static T ExecuteScalarTo<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.ExecuteScalarTo<T>(cmdText, CommandType.Text, paramInfo , parameters, null);\n\n    public static T ExecuteScalarTo<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout=60)\n    {\n        conn.EnsureOpen();\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return command.ExecuteScalarTo<T>();\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<T> ExecuteScalarToAsync<T>(this DbConnection conn, string cmdText, CancellationToken cancellationToken = default) => conn.ExecuteScalarToAsync<T>(cmdText, null, cancellationToken: cancellationToken);\n\n    public static Task<T> ExecuteScalarToAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, CancellationToken cancellationToken = default) => conn.ExecuteScalarToAsync<T>(cmdText, CommandType.Text, paramInfo, null, null, cancellationToken: cancellationToken);\n\n    public static Task<T> ExecuteScalarToAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, CancellationToken cancellationToken = default) => conn.ExecuteScalarToAsync<T>(cmdText, commandType, paramInfo, null, null, cancellationToken: cancellationToken);\n\n    public static Task<T> ExecuteScalarToAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.ExecuteScalarToAsync<T>(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static Task<T> ExecuteScalarToAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, CancellationToken cancellationToken = default)=> conn.ExecuteScalarToAsync<T>(cmdText, commandType, paramInfo, parameters, null, cancellationToken: cancellationToken);\n\n    public static async Task<T> ExecuteScalarToAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout=60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.ExecuteScalarToAsync<T>(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n      public static T? ExecuteScalarToOrDefault<T>(this DbConnection conn, string cmdText) => conn.ExecuteScalarToOrDefault<T>(cmdText,null, null);\n        \n    public static T? ExecuteScalarToOrDefault<T>(this DbConnection conn, string cmdText, object? paramInfo) => conn.ExecuteScalarToOrDefault<T>(cmdText, CommandType.Text, paramInfo,null, null);\n\n    public static T? ExecuteScalarToOrDefault<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo) => conn.ExecuteScalarToOrDefault<T>(cmdText, commandType, paramInfo, null, null);\n\n    public static T? ExecuteScalarToOrDefault<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.ExecuteScalarToOrDefault<T>(cmdText, CommandType.Text, paramInfo , parameters, null);\n\n    public static T? ExecuteScalarToOrDefault<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout=60)\n    {\n        conn.EnsureOpen();\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return command.ExecuteScalarToOrDefault<T>();\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<T?> ExecuteScalarToOrDefaultAsync<T>(this DbConnection conn, string cmdText, CancellationToken cancellationToken = default) => conn.ExecuteScalarToOrDefaultAsync<T>(cmdText, null, cancellationToken: cancellationToken);\n\n    public static Task<T?> ExecuteScalarToOrDefaultAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, CancellationToken cancellationToken = default) => conn.ExecuteScalarToOrDefaultAsync<T>(cmdText, CommandType.Text, paramInfo, null, null, cancellationToken: cancellationToken);\n\n    public static Task<T?> ExecuteScalarToOrDefaultAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, CancellationToken cancellationToken = default) => conn.ExecuteScalarToOrDefaultAsync<T>(cmdText, commandType, paramInfo, null, null, cancellationToken: cancellationToken);\n\n    public static Task<T?> ExecuteScalarToOrDefaultAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.ExecuteScalarToOrDefaultAsync<T>(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static Task<T?> ExecuteScalarToOrDefaultAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, CancellationToken cancellationToken = default)=> conn.ExecuteScalarToOrDefaultAsync<T>(cmdText, commandType, paramInfo, parameters, null, cancellationToken: cancellationToken);\n\n    public static async Task<T?> ExecuteScalarToOrDefaultAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout=60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.ExecuteScalarToOrDefaultAsync<T>(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n      public static IEnumerable<T> QueryColumn<T>(this DbConnection conn, string cmdText, int columnIndex = 0) => conn.QueryColumn<T>(cmdText, null, columnIndex);\n\n    public static IEnumerable<T> QueryColumn<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0) => conn.QueryColumn<T>(cmdText, CommandType.Text, paramInfo, null, null, columnIndex);\n\n    public static IEnumerable<T> QueryColumn<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int columnIndex = 0) => conn.QueryColumn<T>(cmdText, commandType, paramInfo, null, null, columnIndex);\n\n    public static IEnumerable<T> QueryColumn<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0, params DbParameter[]? parameters) => conn.QueryColumn<T>(cmdText, CommandType.Text, paramInfo, parameters, null, columnIndex);\n\n    public static IEnumerable<T> QueryColumn<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int columnIndex = 0) => conn.QueryColumn<T>(cmdText, commandType, paramInfo, parameters, null, columnIndex);\n\n    public static IEnumerable<T> QueryColumn<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, DbTransaction? transaction, int columnIndex = 0, int commandTimeout = 60)\n    {\ntry\n{\n        conn.EnsureOpen();\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            using (var reader = command.ExecuteReader())\n            {\n                var list = new List<T>();\n                while (reader.Read())\n                {\n                    list.Add(reader[columnIndex].To<T>());\n                }\n                return list;\n            }\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<IEnumerable<T>> QueryColumnAsync<T>(this DbConnection conn, string cmdText, int columnIndex = 0, CancellationToken cancellationToken = default)=> conn.QueryColumnAsync<T>(cmdText, null, columnIndex);\n\n    public static Task<IEnumerable<T>> QueryColumnAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0, CancellationToken cancellationToken = default)=> conn.QueryColumnAsync<T>(cmdText, CommandType.Text, paramInfo, null, null, columnIndex);\n\n\n    public static Task<IEnumerable<T>> QueryColumnAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int columnIndex = 0, CancellationToken cancellationToken = default)=> conn.QueryColumnAsync<T>(cmdText, commandType, paramInfo, null, null, columnIndex, cancellationToken: cancellationToken);\n\n    public static Task<IEnumerable<T>> QueryColumnAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0, params DbParameter[]? parameters)=> conn.QueryColumnAsync<T>(cmdText, CommandType.Text, paramInfo, parameters, null, columnIndex);\n\n    public static Task<IEnumerable<T>> QueryColumnAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int columnIndex = 0, CancellationToken cancellationToken = default) => conn.QueryColumnAsync<T>(cmdText, commandType, paramInfo, parameters, null, columnIndex, cancellationToken: cancellationToken);\n\n    public static async Task<IEnumerable<T>> QueryColumnAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, DbTransaction? transaction, int columnIndex = 0, int commandTimeout=60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            using (var reader = command.ExecuteReader())\n            {\n                var list = new List<T>();\n                while (await reader.ReadAsync().ConfigureAwait(false))\n                {\n                    list.Add(reader[columnIndex].To<T>());\n                }\n\n                return list;\n            }\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n      public static IEnumerable<T> SelectColumn<T>(this DbConnection conn, string cmdText, int columnIndex = 0) => conn.SelectColumn<T>(cmdText, null, columnIndex);\n\n    public static IEnumerable<T> SelectColumn<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0) => conn.SelectColumn<T>(cmdText, CommandType.Text, paramInfo, null, null, columnIndex);\n\n    public static IEnumerable<T> SelectColumn<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int columnIndex = 0) => conn.SelectColumn<T>(cmdText, commandType, paramInfo, null, null, columnIndex);\n\n    public static IEnumerable<T> SelectColumn<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0, params DbParameter[]? parameters) => conn.SelectColumn<T>(cmdText, CommandType.Text, paramInfo, parameters, null, columnIndex);\n\n    public static IEnumerable<T> SelectColumn<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int columnIndex = 0) => conn.SelectColumn<T>(cmdText, commandType, paramInfo, parameters, null, columnIndex);\n\n    public static IEnumerable<T> SelectColumn<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, DbTransaction? transaction, int columnIndex = 0, int commandTimeout = 60)\n    {\ntry\n{\n        conn.EnsureOpen();\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            using (var reader = command.ExecuteReader())\n            {\n                var list = new List<T>();\n                while (reader.Read())\n                {\n                    list.Add(reader[columnIndex].To<T>());\n                }\n                return list;\n            }\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<IEnumerable<T>> SelectColumnAsync<T>(this DbConnection conn, string cmdText, int columnIndex = 0, CancellationToken cancellationToken = default)=> conn.SelectColumnAsync<T>(cmdText, null, columnIndex);\n\n    public static Task<IEnumerable<T>> SelectColumnAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0, CancellationToken cancellationToken = default)=> conn.SelectColumnAsync<T>(cmdText, CommandType.Text, paramInfo, null, null, columnIndex);\n\n\n    public static Task<IEnumerable<T>> SelectColumnAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int columnIndex = 0, CancellationToken cancellationToken = default)=> conn.SelectColumnAsync<T>(cmdText, commandType, paramInfo, null, null, columnIndex, cancellationToken: cancellationToken);\n\n    public static Task<IEnumerable<T>> SelectColumnAsync<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0, params DbParameter[]? parameters)=> conn.SelectColumnAsync<T>(cmdText, CommandType.Text, paramInfo, parameters, null, columnIndex);\n\n    public static Task<IEnumerable<T>> SelectColumnAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int columnIndex = 0, CancellationToken cancellationToken = default) => conn.SelectColumnAsync<T>(cmdText, commandType, paramInfo, parameters, null, columnIndex, cancellationToken: cancellationToken);\n\n    public static async Task<IEnumerable<T>> SelectColumnAsync<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, DbTransaction? transaction, int columnIndex = 0, int commandTimeout=60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            using (var reader = command.ExecuteReader())\n            {\n                var list = new List<T>();\n                while (await reader.ReadAsync().ConfigureAwait(false))\n                {\n                    list.Add(reader[columnIndex].To<T>());\n                }\n\n                return list;\n            }\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/DbConnectionExtension.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n<#@ output extension=\".generated.cs\" #>\nusing System.Data;\nusing System.Data.Common;\n\nnamespace WeihanLi.Extensions;\n#nullable enable\npublic static partial class DataExtension\n{\n    <#\nvar commandNames = new Tuple<string, string, string>[]{\n    Tuple.Create(\"Execute\",\"int\",\"ExecuteNonQuery\"),\n    Tuple.Create(\"ExecuteScalar\",\"object?\",\"ExecuteScalar\"),        \n    Tuple.Create(\"Fetch\",\"dynamic\",\"Fetch\"),\n    Tuple.Create(\"ExecuteDataTable\",\"DataTable\",\"ExecuteDataTable\"),        \n};\n\nvar queryCommand= new Tuple<string,string,string>[]{\n    Tuple.Create(\"Fetch\", \"T?\", \"ToEntity\"),\n};\n\nvar deferQueryCommand= new Tuple<string,string,string>[]{\n    Tuple.Create(\"Select\", \"IEnumerable<T>\", \"<T>\"),     \n    Tuple.Create(\"Select\", \"IEnumerable<dynamic>\", \"\")\n};\n\nvar queryDataTableCommand= new Tuple<string,string>[]{\n    Tuple.Create(\"ExecuteDataTable\",\"T\")\n};\nvar queryScalarCommand= new Tuple<string,string, string>[]{\n    Tuple.Create(\"ExecuteScalarTo\",\"To\", \"T\"),\n    Tuple.Create(\"ExecuteScalarToOrDefault\",\"ToOrDefault\", \"T?\"),\n    };\nvar queryColumnCommand = new string[]{ \"QueryColumn\", \"SelectColumn\" };\nforeach(var cmd in commandNames)\n{ \n#>public static <#=            cmd.Item2 #> <#=            cmd.Item1 #>(this DbConnection conn, string cmdText, int commandTimeout = 60) => conn.<#=            cmd.Item1 #>(cmdText, null, commandTimeout);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #>(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60) => conn.<#=            cmd.Item1 #>(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.<#=            cmd.Item1 #>(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout = 60)=> conn.<#=            cmd.Item1 #>(cmdText, commandType, paramInfo, parameters, null, commandTimeout);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60)\n    {\n      conn.EnsureOpen();\n      try\n      {\n        using var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout);\n        return command.<#=            cmd.Item3 #>();\n      }\n      finally\n      {\n        conn.Close();\n      }\n    }\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async(this DbConnection conn, string cmdText, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.<#=            cmd.Item1 #>Async(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static async Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{        \nusing (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.<#=            cmd.Item3 #>Async(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n<#    }#>\n\n<#\nforeach(var cmd in queryCommand)\n{ \n#>   public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, int commandTimeout = 60)  => conn.<#=            cmd.Item1 #><T>(cmdText, CommandType.Text, null, commandTimeout);\n        \n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60)  => conn.<#=            cmd.Item1 #><T>(cmdText, CommandType.Text, paramInfo,null, null, commandTimeout);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60)  => conn.<#=            cmd.Item1 #><T>(cmdText, commandType, paramInfo, null, null, commandTimeout);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.<#=            cmd.Item1 #><T>(cmdText, CommandType.Text, paramInfo , parameters, null);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60) \n    {\n        conn.EnsureOpen();\n        try\n        {\n            using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n            {\n                return command.<#=            cmd.Item1 #><T>();\n            }\n        }\n        finally\n        {\n            conn.Close();\n        }\n    }\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.<#=            cmd.Item1 #>Async<T>(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.<#=            cmd.Item1 #>Async<T>(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.<#=            cmd.Item1 #>Async<T>(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.<#=            cmd.Item1 #>Async<T>(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout=60, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async<T>(cmdText, commandType, paramInfo, parameters, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static async Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default) \n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.<#=            cmd.Item1 #>Async<T>(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n<#    }#>\n\n\n<#\nforeach(var cmd in deferQueryCommand)\n{ \n#>   public static <#=            cmd.Item2 #> <#= cmd.Item1 #><#= cmd.Item3 #>(this DbConnection conn, string cmdText, int commandTimeout = 60)  => conn.<#= cmd.Item1 #><#= cmd.Item3 #>(cmdText, CommandType.Text, null, commandTimeout);\n        \n    public static <#=            cmd.Item2 #> <#= cmd.Item1 #><#= cmd.Item3 #>(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout = 60)  => conn.<#= cmd.Item1 #><#= cmd.Item3 #>(cmdText, CommandType.Text, paramInfo,null, null, commandTimeout);\n\n    public static <#=            cmd.Item2 #> <#= cmd.Item1 #><#= cmd.Item3 #>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout = 60)  => conn.<#= cmd.Item1 #><#= cmd.Item3 #>(cmdText, commandType, paramInfo, null, null, commandTimeout);\n\n    public static <#=            cmd.Item2 #> <#= cmd.Item1 #><#= cmd.Item3 #>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.<#= cmd.Item1 #><#= cmd.Item3 #>(cmdText, CommandType.Text, paramInfo , parameters, null);\n\n    public static <#=            cmd.Item2 #> <#= cmd.Item1 #><#= cmd.Item3 #>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60) \n    {\n        conn.EnsureOpen();\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return command.<#= cmd.Item1 #><#= cmd.Item3 #>();\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<#= cmd.Item3 #>(this DbConnection conn, string cmdText, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.<#=            cmd.Item1 #>Async<#= cmd.Item3 #>(cmdText, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<#= cmd.Item3 #>(this DbConnection conn, string cmdText, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.<#=            cmd.Item1 #>Async<#= cmd.Item3 #>(cmdText, CommandType.Text, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<#= cmd.Item3 #>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int commandTimeout=60, CancellationToken cancellationToken = default)  => conn.<#=            cmd.Item1 #>Async<#= cmd.Item3 #>(cmdText, commandType, paramInfo, null, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<#= cmd.Item3 #>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters) => conn.<#=            cmd.Item1 #>Async<#= cmd.Item3 #>(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<#= cmd.Item3 #>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int commandTimeout=60, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async<#= cmd.Item3 #>(cmdText, commandType, paramInfo, parameters, null, commandTimeout, cancellationToken: cancellationToken);\n\n    public static async Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<#= cmd.Item3 #>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout = 60, CancellationToken cancellationToken = default) \n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.<#=            cmd.Item1 #>Async<#= cmd.Item3 #>(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n<#    }#>\n\n\n<#\nforeach(var cmd in queryDataTableCommand)\n{ \n#>      public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, Func<DataTable, T> func)  => conn.<#=            cmd.Item1 #><T>(cmdText,null, null, func);\n        \n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, object? paramInfo, Func<DataTable, T> func)  => conn.<#=            cmd.Item1 #><T>(cmdText, CommandType.Text, paramInfo,null, null, func);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, Func<DataTable, T> func)  => conn.<#=            cmd.Item1 #><T>(cmdText, commandType, paramInfo, null, null, func);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, object? paramInfo, DbParameter[]? parameters, Func<DataTable, T> func) => conn.<#=            cmd.Item1 #><T>(cmdText, CommandType.Text, paramInfo , parameters, null, func);\n\n    public static <#=            cmd.Item2 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, DbTransaction? transaction, Func<DataTable, T> func, int commandTimeout = 60) \n    {\n        conn.EnsureOpen();\ntry{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return command.ExecuteDataTable(func);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText,Func<DataTable, T> func, CancellationToken cancellationToken = default)  => conn.<#=            cmd.Item1 #>Async<T>(cmdText, null, func, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, object? paramInfo,Func<DataTable, T> func, CancellationToken cancellationToken = default)  => conn.<#=            cmd.Item1 #>Async<T>(cmdText, CommandType.Text, paramInfo, null, null, func, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo,Func<DataTable, T> func, CancellationToken cancellationToken = default)  => conn.<#=            cmd.Item1 #>Async<T>(cmdText, commandType, paramInfo, null, null, func, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, object? paramInfo, DbParameter[]? parameters,Func<DataTable, T> func, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async<T>(cmdText, CommandType.Text, paramInfo, parameters, null, func, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,Func<DataTable, T> func, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async<T>(cmdText, commandType, paramInfo, parameters, func, cancellationToken: cancellationToken);\n\n    public static async Task<<#=            cmd.Item2 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction,Func<DataTable, T> func, int commandTimeout = 60, CancellationToken cancellationToken = default) \n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return func(await command.ExecuteDataTableAsync(cancellationToken));\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n<#    }#>\n\n<#\nforeach(var cmd in queryScalarCommand)\n{ \n#>      public static <#=            cmd.Item3 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText) => conn.<#=            cmd.Item1 #><T>(cmdText,null, null);\n        \n    public static <#=            cmd.Item3 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, object? paramInfo) => conn.<#=            cmd.Item1 #><T>(cmdText, CommandType.Text, paramInfo,null, null);\n\n    public static <#=            cmd.Item3 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo) => conn.<#=            cmd.Item1 #><T>(cmdText, commandType, paramInfo, null, null);\n\n    public static <#=            cmd.Item3 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.<#=            cmd.Item1 #><T>(cmdText, CommandType.Text, paramInfo , parameters, null);\n\n    public static <#=            cmd.Item3 #> <#=            cmd.Item1 #><T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout=60)\n    {\n        conn.EnsureOpen();\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return command.<#=            cmd.Item1 #><T>();\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<<#=            cmd.Item3 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async<T>(cmdText, null, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item3 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, object? paramInfo, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async<T>(cmdText, CommandType.Text, paramInfo, null, null, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item3 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, CancellationToken cancellationToken = default) => conn.<#=            cmd.Item1 #>Async<T>(cmdText, commandType, paramInfo, null, null, cancellationToken: cancellationToken);\n\n    public static Task<<#=            cmd.Item3 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, object? paramInfo, params DbParameter[]? parameters)=> conn.<#=            cmd.Item1 #>Async<T>(cmdText, CommandType.Text, paramInfo, parameters, null);\n\n    public static Task<<#=            cmd.Item3 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, CancellationToken cancellationToken = default)=> conn.<#=            cmd.Item1 #>Async<T>(cmdText, commandType, paramInfo, parameters, null, cancellationToken: cancellationToken);\n\n    public static async Task<<#=            cmd.Item3 #>> <#=            cmd.Item1 #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters,DbTransaction? transaction, int commandTimeout=60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            return await command.<#=            cmd.Item1 #>Async<T>(cancellationToken);\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n<#    }#>\n\n<#\nforeach(var cmd in queryColumnCommand)\n{ \n#>      public static IEnumerable<T> <#=            cmd #><T>(this DbConnection conn, string cmdText, int columnIndex = 0) => conn.<#=            cmd #><T>(cmdText, null, columnIndex);\n\n    public static IEnumerable<T> <#=            cmd #><T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0) => conn.<#=            cmd #><T>(cmdText, CommandType.Text, paramInfo, null, null, columnIndex);\n\n    public static IEnumerable<T> <#=            cmd #><T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int columnIndex = 0) => conn.<#=            cmd #><T>(cmdText, commandType, paramInfo, null, null, columnIndex);\n\n    public static IEnumerable<T> <#=            cmd #><T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0, params DbParameter[]? parameters) => conn.<#=            cmd #><T>(cmdText, CommandType.Text, paramInfo, parameters, null, columnIndex);\n\n    public static IEnumerable<T> <#=            cmd #><T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int columnIndex = 0) => conn.<#=            cmd #><T>(cmdText, commandType, paramInfo, parameters, null, columnIndex);\n\n    public static IEnumerable<T> <#=            cmd #><T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, DbTransaction? transaction, int columnIndex = 0, int commandTimeout = 60)\n    {\ntry\n{\n        conn.EnsureOpen();\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            using (var reader = command.ExecuteReader())\n            {\n                var list = new List<T>();\n                while (reader.Read())\n                {\n                    list.Add(reader[columnIndex].To<T>());\n                }\n                return list;\n            }\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n\n    public static Task<IEnumerable<T>> <#=            cmd #>Async<T>(this DbConnection conn, string cmdText, int columnIndex = 0, CancellationToken cancellationToken = default)=> conn.<#=            cmd #>Async<T>(cmdText, null, columnIndex);\n\n    public static Task<IEnumerable<T>> <#=            cmd #>Async<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0, CancellationToken cancellationToken = default)=> conn.<#=            cmd #>Async<T>(cmdText, CommandType.Text, paramInfo, null, null, columnIndex);\n\n\n    public static Task<IEnumerable<T>> <#=            cmd #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, int columnIndex = 0, CancellationToken cancellationToken = default)=> conn.<#=            cmd #>Async<T>(cmdText, commandType, paramInfo, null, null, columnIndex, cancellationToken: cancellationToken);\n\n    public static Task<IEnumerable<T>> <#=            cmd #>Async<T>(this DbConnection conn, string cmdText, object? paramInfo, int columnIndex = 0, params DbParameter[]? parameters)=> conn.<#=            cmd #>Async<T>(cmdText, CommandType.Text, paramInfo, parameters, null, columnIndex);\n\n    public static Task<IEnumerable<T>> <#=            cmd #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, int columnIndex = 0, CancellationToken cancellationToken = default) => conn.<#=            cmd #>Async<T>(cmdText, commandType, paramInfo, parameters, null, columnIndex, cancellationToken: cancellationToken);\n\n    public static async Task<IEnumerable<T>> <#=            cmd #>Async<T>(this DbConnection conn, string cmdText, CommandType commandType, object? paramInfo, DbParameter[]? parameters, DbTransaction? transaction, int columnIndex = 0, int commandTimeout=60, CancellationToken cancellationToken = default)\n    {\n        await conn.EnsureOpenAsync().ConfigureAwait(false);\ntry\n{\n        using (var command = conn.GetDbCommand(cmdText,commandType: commandType, paramInfo: paramInfo, parameters: parameters, transaction: transaction, commandTimeout: commandTimeout))\n        {\n            using (var reader = command.ExecuteReader())\n            {\n                var list = new List<T>();\n                while (await reader.ReadAsync().ConfigureAwait(false))\n                {\n                    list.Add(reader[columnIndex].To<T>());\n                }\n\n                return list;\n            }\n        }\n}\nfinally{\nconn.Close();\n}\n    }\n<#    }#>\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/DictionaryExtension.cs",
    "content": "﻿using System.Collections.Specialized;\nusing System.Data;\nusing System.Data.Common;\nusing System.Text;\nusing WeihanLi.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\n/// <summary>\n/// DictionaryExtension\n/// </summary>\npublic static class DictionaryExtension\n{\n    /// <summary>\n    /// 根据key获取Dictionary中元素\n    /// </summary>\n    /// <typeparam name=\"TKey\">key类型</typeparam>\n    /// <typeparam name=\"TValue\">value类型</typeparam>\n    /// <param name=\"dictionary\">字典</param>\n    /// <param name=\"key\">key</param>\n    /// <param name=\"value\">value</param>\n    /// <param name=\"defaultValue\">默认值</param>\n    /// <returns></returns>\n    public static bool TryGetValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, out TValue value, TValue defaultValue)\n    {\n        if (dictionary.TryGetValue(key, out var result))\n        {\n            value = result;\n            return true;\n        }\n        else\n        {\n            value = defaultValue;\n            return false;\n        }\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;TKey,TValue&gt; extension method that adds if not contains key.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"key\">The key.</param>\n    /// <param name=\"value\">The value.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool AddIfNotContainsKey<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, TValue value)\n    {\n        if (!@this.ContainsKey(key))\n        {\n            @this.Add(key, value);\n            return true;\n        }\n\n        return false;\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;TKey,TValue&gt; extension method that adds if not contains key.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"key\">The key.</param>\n    /// <param name=\"valueFactory\">The value factory.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool AddIfNotContainsKey<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, Func<TValue> valueFactory)\n    {\n        if (!@this.ContainsKey(key))\n        {\n            @this.Add(key, valueFactory());\n            return true;\n        }\n\n        return false;\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;TKey,TValue&gt; extension method that adds if not contains key.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"key\">The key.</param>\n    /// <param name=\"valueFactory\">The value factory.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool AddIfNotContainsKey<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, Func<TKey, TValue> valueFactory)\n    {\n        if (!@this.ContainsKey(key))\n        {\n            @this.Add(key, valueFactory(key));\n            return true;\n        }\n\n        return false;\n    }\n\n    /// <summary>\n    ///     Adds a key/value pair to the IDictionary&lt;TKey, TValue&gt; if the key does not already exist.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"key\">The key of the element to add.</param>\n    /// <param name=\"value\">The value to be added, if the key does not already exist.</param>\n    /// <returns>\n    ///     The value for the key. This will be either the existing value for the key if the key is already in the\n    ///     dictionary, or the new value if the key was not in the dictionary.\n    /// </returns>\n    public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, TValue value)\n    {\n        if (!@this.TryGetValue(key, out var val))\n        {\n            @this.Add(key, value);\n            val = value;\n        }\n        return val;\n    }\n\n    /// <summary>\n    ///     Adds a key/value pair to the IDictionary&lt;TKey, TValue&gt; by using the specified function, if the key does\n    ///     not already exist.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"key\">The key of the element to add.</param>\n    /// <param name=\"valueFactory\">TThe function used to generate a value for the key.</param>\n    /// <returns>\n    ///     The value for the key. This will be either the existing value for the key if the key is already in the\n    ///     dictionary, or the new value for the key as returned by valueFactory if the key was not in the dictionary.\n    /// </returns>\n    public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, Func<TKey, TValue> valueFactory)\n    {\n        if (!@this.ContainsKey(key))\n        {\n            @this.Add(new KeyValuePair<TKey, TValue>(key, valueFactory(key)));\n        }\n\n        return @this[key];\n    }\n\n    /// <summary>\n    ///     Uses the specified functions to add a key/value pair to the IDictionary&lt;TKey, TValue&gt; if the key does\n    ///     not already exist, or to update a key/value pair in the IDictionary&lt;TKey, TValue&gt;> if the key already\n    ///     exists.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"key\">The key to be added or whose value should be updated.</param>\n    /// <param name=\"value\">The value to be added or updated.</param>\n    /// <returns>The new value for the key.</returns>\n    public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, TValue value)\n    {\n        if (!@this.ContainsKey(key))\n        {\n            @this.Add(new KeyValuePair<TKey, TValue>(key, value));\n        }\n        else\n        {\n            @this[key] = value;\n        }\n\n        return @this[key];\n    }\n\n    /// <summary>\n    ///     Uses the specified functions to add a key/value pair to the IDictionary&lt;TKey, TValue&gt; if the key does\n    ///     not already exist, or to update a key/value pair in the IDictionary&lt;TKey, TValue&gt;> if the key already\n    ///     exists.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"key\">The key to be added or whose value should be updated.</param>\n    /// <param name=\"addValue\">The value to be added for an absent key.</param>\n    /// <param name=\"updateValueFactory\">\n    ///     The function used to generate a new value for an existing key based on the key's\n    ///     existing value.\n    /// </param>\n    /// <returns>\n    ///     The new value for the key. This will be either be addValue (if the key was absent) or the result of\n    ///     updateValueFactory (if the key was present).\n    /// </returns>\n    public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)\n    {\n        if (!@this.TryGetValue(key, out var value))\n        {\n            @this.Add(new KeyValuePair<TKey, TValue>(key, addValue));\n        }\n        else\n        {\n            @this[key] = updateValueFactory(key, value);\n        }\n\n        return @this[key];\n    }\n\n    /// <summary>\n    ///     Uses the specified functions to add a key/value pair to the IDictionary&lt;TKey, TValue&gt; if the key does\n    ///     not already exist, or to update a key/value pair in the IDictionary&lt;TKey, TValue&gt;> if the key already\n    ///     exists.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"key\">The key to be added or whose value should be updated.</param>\n    /// <param name=\"addValueFactory\">The function used to generate a value for an absent key.</param>\n    /// <param name=\"updateValueFactory\">\n    ///     The function used to generate a new value for an existing key based on the key's\n    ///     existing value.\n    /// </param>\n    /// <returns>\n    ///     The new value for the key. This will be either be the result of addValueFactory (if the key was absent) or\n    ///     the result of updateValueFactory (if the key was present).\n    /// </returns>\n    public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)\n    {\n        if (!@this.TryGetValue(key, out var value))\n        {\n            @this.Add(new KeyValuePair<TKey, TValue>(key, addValueFactory(key)));\n        }\n        else\n        {\n            @this[key] = updateValueFactory(key, value);\n        }\n\n        return @this[key];\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;TKey,TValue&gt; extension method that removes if contains key.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"key\">The key.</param>\n    public static void RemoveIfContainsKey<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key)\n    {\n        if (@this.ContainsKey(key))\n        {\n            @this.Remove(key);\n        }\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;TKey,TValue&gt; extension method that converts the @this to a sorted dictionary.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as a SortedDictionary&lt;TKey,TValue&gt;</returns>\n    public static SortedDictionary<TKey, TValue> ToSortedDictionary<TKey, TValue>(this IDictionary<TKey, TValue> @this) where TKey : notnull\n    {\n        return new(@this);\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;TKey,TValue&gt; extension method that converts the @this to a sorted dictionary.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"comparer\">The comparer.</param>\n    /// <returns>@this as a SortedDictionary&lt;TKey,TValue&gt;</returns>\n    public static SortedDictionary<TKey, TValue> ToSortedDictionary<TKey, TValue>(this IDictionary<TKey, TValue> @this, IComparer<TKey> comparer) where TKey : notnull\n    {\n        return new(@this, comparer);\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;TKey,TValue&gt; extension method that query if '@this' contains any key.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"keys\">A variable-length parameters list containing keys.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool ContainsAnyKey<TKey, TValue>(this IDictionary<TKey, TValue> @this, params TKey[] keys) where TKey : notnull\n    {\n        foreach (var value in keys)\n        {\n            if (@this.ContainsKey(value))\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;TKey,TValue&gt; extension method that query if '@this' contains all key.\n    /// </summary>\n    /// <typeparam name=\"TKey\">Type of the key.</typeparam>\n    /// <typeparam name=\"TValue\">Type of the value.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"keys\">A variable-length parameters list containing keys.</param>\n    /// <returns>true if it succeeds, false if it fails.</returns>\n    public static bool ContainsAllKey<TKey, TValue>(this IDictionary<TKey, TValue> @this, params TKey[] keys)\n    {\n        foreach (var value in keys)\n        {\n            if (!@this.ContainsKey(value))\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;string,string&gt; extension method that converts the @this to a name value collection.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>@this as a NameValueCollection.</returns>\n    public static NameValueCollection ToNameValueCollection(this IDictionary<string, string>? @this)\n    {\n        if (@this == null)\n        {\n            return [];\n        }\n        var col = new NameValueCollection();\n        foreach (var item in @this)\n        {\n            col.Add(item.Key, item.Value);\n        }\n        return col;\n    }\n\n    public static NameValueCollection ToNameValueCollection(this IEnumerable<KeyValuePair<string, string>>? source)\n    {\n        if (source == null)\n        {\n            return [];\n        }\n\n        var collection = new NameValueCollection();\n\n        foreach (var item in source)\n        {\n            if (string.IsNullOrWhiteSpace(item.Key))\n            {\n                continue;\n            }\n            collection.Add(item.Key, item.Value);\n        }\n\n        return collection;\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;string,object&gt; extension method that converts this object to a database parameters.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"command\">The command.</param>\n    /// <returns>The given data converted to a DbParameter[].</returns>\n    public static DbParameter[] ToDbParameters(this IDictionary<string, object> @this, DbCommand command)\n    {\n        return @this.Select(x =>\n        {\n            var parameter = command.CreateParameter();\n            parameter.ParameterName = x.Key;\n            parameter.Value = x.Value;\n            return parameter;\n        }).ToArray();\n    }\n\n    /// <summary>\n    ///     An IDictionary&lt;string,object&gt; extension method that converts this object to a database parameters.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"connection\">The connection.</param>\n    /// <returns>The given data converted to a DbParameter[].</returns>\n    public static DbParameter[] ToDbParameters(this IDictionary<string, object> @this, DbConnection connection)\n    {\n        var command = connection.CreateCommand();\n\n        return @this.Select(x =>\n        {\n            var parameter = command.CreateParameter();\n            parameter.ParameterName = x.Key;\n            parameter.Value = x.Value;\n            return parameter;\n        }).ToArray();\n    }\n\n    /// <summary>\n    /// IDictionary to dataTable\n    /// </summary>\n    /// <param name=\"dictionary\">IDictionary</param>\n    /// <returns></returns>\n    public static DataTable ToDataTable(this IDictionary<string, object> dictionary)\n    {\n        Guard.NotNull(dictionary);\n        var dataTable = new DataTable();\n        if (dictionary.Keys.Count == 0)\n        {\n            return dataTable;\n        }\n        dataTable.Columns.AddRange(dictionary.Keys.Select(key => new DataColumn(key, dictionary[key].GetType())).ToArray());\n        foreach (var key in dictionary.Keys)\n        {\n            var row = dataTable.NewRow();\n            row[key] = dictionary[key];\n            dataTable.Rows.Add(row);\n        }\n        return dataTable;\n    }\n\n    public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source) where TKey : notnull => source.ToDictionary(pair => pair.Key, pair => pair.Value);\n\n    public static IEnumerable<KeyValuePair<string, string?>> ToKeyValuePair(this NameValueCollection? collection)\n    {\n        if (collection == null || collection.Count == 0)\n        {\n            yield break;\n        }\n\n        foreach (var key in collection.AllKeys)\n        {\n            if (string.IsNullOrWhiteSpace(key))\n            {\n                continue;\n            }\n\n            yield return new KeyValuePair<string, string?>(key, collection[key]);\n        }\n    }\n\n    public static string ToQueryString(this IEnumerable<KeyValuePair<string, string>>? source)\n    {\n        if (source == null)\n        {\n            return string.Empty;\n        }\n\n        var sb = new StringBuilder();\n\n        foreach (var item in source)\n        {\n            if (string.IsNullOrWhiteSpace(item.Key))\n            {\n                continue;\n            }\n            sb.Append('&');\n            sb.Append(item.Key.UrlEncode());\n            sb.Append('=');\n            if (item.Value.IsNotNullOrEmpty())\n                sb.Append(item.Value.UrlEncode());\n        }\n\n        return sb.Length > 1 ? sb.ToString(1, sb.Length - 1) : string.Empty;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/DumpExtension.cs",
    "content": "﻿using WeihanLi.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions.Dump;\n\npublic static class DumpExtension\n{\n    private const string NullValue = \"(null)\";\n\n    public static void Dump<T>(this T t) => Dump(t, Console.WriteLine);\n\n    public static void Dump<T>(this T t, Action<string> dumpAction)\n    {\n        Guard.NotNull(dumpAction, nameof(dumpAction))\n            .Invoke(t is null ? NullValue : t.ToJsonOrString());\n    }\n\n    public static void Dump<T>(this T t, Action<string> dumpAction, Func<T, string> dumpValueFactory)\n    {\n        Guard.NotNull(dumpAction, nameof(dumpAction))\n            .Invoke(Guard.NotNull(dumpValueFactory, nameof(dumpValueFactory)).Invoke(t));\n    }\n\n    public static Task DumpAsync<T>(this T t, Func<string, Task> dumpAction)\n    {\n        return Guard.NotNull(dumpAction, nameof(dumpAction))\n            .Invoke(t is null ? NullValue : t.ToJsonOrString());\n    }\n\n    public static Task DumpAsync<T>(this T t, Func<string, Task> dumpAction, Func<T, string> dumpValueFactory)\n    {\n        return Guard.NotNull(dumpAction, nameof(dumpAction))\n            .Invoke(Guard.NotNull(dumpValueFactory, nameof(dumpValueFactory)).Invoke(t));\n    }\n\n    public static ValueTask DumpAsync<T>(this T t, Func<string, ValueTask> dumpAction)\n    {\n        return Guard.NotNull(dumpAction, nameof(dumpAction))\n            .Invoke(t is null ? NullValue : t.ToJsonOrString());\n    }\n\n    public static ValueTask DumpAsync<T>(this T t, Func<string, ValueTask> dumpAction, Func<T, string> dumpValueFactory)\n    {\n        return Guard.NotNull(dumpAction, nameof(dumpAction))\n            .Invoke(Guard.NotNull(dumpValueFactory, nameof(dumpValueFactory)).Invoke(t));\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/EnumerableExtension.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System;\nusing System.Collections;\nusing System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers.Combinatorics;\nusing WeihanLi.Common.Models;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class EnumerableExtension\n{\n    public static void ForEach<T>(this IEnumerable<T> ts, Action<T> action)\n    {\n        foreach (var t in ts)\n        {\n            action(t);\n        }\n    }\n\n    public static void ForEach<T>(this IEnumerable<T> ts, Action<T, int> action)\n    {\n        var i = 0;\n        foreach (var t in ts)\n        {\n            action(t, i);\n            i++;\n        }\n    }\n\n    public static async Task ForEachAsync<T>(this IEnumerable<T> ts, Func<T, Task> action)\n    {\n        foreach (var t in ts)\n        {\n            await action(t);\n        }\n    }\n\n    public static async Task ForEachAsync<T>(this IEnumerable<T> ts, Func<T, int, Task> action)\n    {\n        var i = 0;\n        foreach (var t in ts)\n        {\n            await action(t, i);\n            i++;\n        }\n    }\n\n    /// <summary>\n    ///     A T[] extension method that converts an array to a read only.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>A list of.</returns>\n    public static IReadOnlyCollection<T> AsReadOnly<T>(this IEnumerable<T> @this)\n    {\n        return Array.AsReadOnly(@this.ToArray());\n    }\n\n    /// <summary>\n    ///     An IEnumerable&lt;T&gt; extension method that queries if a not null or is empty.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"source\">The collection to act on.</param>\n    /// <returns>true if a not null or is t>, false if not.</returns>\n    public static bool HasValue<T>([NotNullWhen(true)] this IEnumerable<T>? source)\n    {\n        return source != null && source.Any();\n    }\n\n    /// <summary>\n    ///     Concatenates all the elements of a IEnumerable, using the specified separator between each element.\n    /// </summary>\n    /// <typeparam name=\"T\">Generic type parameter.</typeparam>\n    /// <param name=\"this\">An IEnumerable that contains the elements to concatenate.</param>\n    /// <param name=\"separator\">\n    ///     The string to use as a separator. separator is included in the returned string only if\n    ///     value has more than one element.\n    /// </param>\n    /// <returns>\n    ///     A string that consists of the elements in value delimited by the separator string. If value is an empty array,\n    ///     the method returns String.Empty.\n    /// </returns>\n    public static string StringJoin<T>(this IEnumerable<T> @this, string separator)\n    {\n        return string.Join(separator, @this);\n    }\n\n#if NETSTANDARD2_0\n\n    public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource value)\n    {\n        yield return value;\n\n        foreach (var element in source)\n        {\n            yield return element;\n        }\n    }\n\n    public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource value)\n    {\n        foreach (var element in source)\n        {\n            yield return element;\n        }\n\n        yield return value;\n    }\n\n#endif\n\n    #region Split\n\n    /// <summary>\n    /// 将一维集合分割成二维集合\n    /// </summary>\n    /// <param name=\"source\">source</param>\n    /// <param name=\"batchSize\">每个一维集合的数量</param>\n    public static IEnumerable<T[]> Split<T>(this IEnumerable<T> source, int batchSize)\n    {\n        using var enumerator = source.GetEnumerator();\n        while (enumerator.MoveNext())\n        {\n            yield return Split(enumerator, batchSize).ToArray();\n        }\n    }\n\n    private static IEnumerable<T> Split<T>(IEnumerator<T> enumerator, int batchSize)\n    {\n        do\n        {\n            yield return enumerator.Current;\n        } while (--batchSize > 0 && enumerator.MoveNext());\n    }\n\n    #endregion Split\n\n    public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Func<T, bool> predict, bool condition)\n        => condition ? Guard.NotNull(source, nameof(source)).Where(predict) : source;\n\n    public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Func<T, bool> predict, Func<bool> condition)\n        => condition() ? Guard.NotNull(source, nameof(source)).Where(predict) : source;\n\n    [return: NotNull]\n    public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class\n        => Guard.NotNull(source).Where(x => x != null)!;\n\n    public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> source)\n        => Guard.NotNull(source).SelectMany(x => x);\n\n    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T?, T?, bool> comparer) where T : class\n        => source.Distinct(new DynamicEqualityComparer<T>(comparer));\n\n    // https://github.com/aspnet/EntityFrameworkCore/blob/release/3.0/src/EFCore.SqlServer/Utilities/EnumerableExtensions.cs\n    private sealed class DynamicEqualityComparer<T>(Func<T?, T?, bool> func) : IEqualityComparer<T>\n        where T : class\n    {\n        private readonly Func<T?, T?, bool> _func = func;\n\n        public bool Equals(T? x, T? y) => _func(x, y);\n\n        public int GetHashCode(T obj) => 0; // force Equals\n    }\n\n    #region Linq\n\n#if !NET10_OR_GREATER\n    /// <summary>\n    /// LeftJoin extension\n    /// </summary>\n    /// <typeparam name=\"TOuter\">outer</typeparam>\n    /// <typeparam name=\"TInner\">inner</typeparam>\n    /// <typeparam name=\"TKey\">TKey</typeparam>\n    /// <typeparam name=\"TResult\">TResult</typeparam>\n    /// <param name=\"outer\">outer collection</param>\n    /// <param name=\"inner\">inner collection</param>\n    /// <param name=\"outerKeySelector\">outerKeySelector</param>\n    /// <param name=\"innerKeySelector\">innerKeySelector</param>\n    /// <param name=\"resultSelector\">resultSelector</param>\n    /// <returns></returns>\n    public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer,\n        IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector,\n        Func<TOuter, TInner?, TResult> resultSelector)\n    {\n        return outer\n            .GroupJoin(inner, outerKeySelector, innerKeySelector,\n                (outerObj, inners) => new { outerObj, inners = inners.DefaultIfEmpty() })\n            .SelectMany(a => a.inners.Select(innerObj => resultSelector(a.outerObj, innerObj)));\n    }\n#endif\n\n    #endregion Linq\n\n    #region ToPagedList\n\n    /// <summary>\n    /// ToPagedList\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"data\">data</param>\n    /// <param name=\"totalCount\">totalCount</param>\n    /// <returns></returns>\n    public static IListResultWithTotal<T> ToListResultWithTotal<T>(this IEnumerable<T> data, int totalCount)\n        => new ListResultWithTotal<T>\n        {\n            TotalCount = totalCount,\n            Data = data is IReadOnlyList<T> dataList ? dataList : data.ToArray()\n        };\n\n    /// <summary>\n    /// ToPagedList\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"data\">data</param>\n    /// <param name=\"pageNumber\">pageNumber</param>\n    /// <param name=\"pageSize\">pageSize</param>\n    /// <param name=\"totalCount\">totalCount</param>\n    /// <returns></returns>\n    public static IPagedListResult<T> ToPagedList<T>(this IEnumerable<T> data, int pageNumber, int pageSize,\n        int totalCount)\n        => new PagedListResult<T>\n        {\n            PageNumber = pageNumber,\n            PageSize = pageSize,\n            TotalCount = totalCount,\n            Data = data is IReadOnlyList<T> dataList ? dataList : data.ToArray()\n        };\n\n    /// <summary>\n    /// ToPagedList\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"data\">data</param>\n    /// <param name=\"pageNumber\">pageNumber</param>\n    /// <param name=\"pageSize\">pageSize</param>\n    /// <param name=\"totalCount\">totalCount</param>\n    /// <returns></returns>\n    public static IPagedListResult<T> ToPagedList<T>(this IReadOnlyList<T> data, int pageNumber, int pageSize,\n        int totalCount)\n        => new PagedListResult<T>\n        {\n            PageNumber = pageNumber,\n            PageSize = pageSize,\n            TotalCount = totalCount,\n            Data = data\n        };\n\n    #endregion ToPagedList\n\n    public static IEnumerable<IReadOnlyList<T>> GetCombinations<T>(this IEnumerable<T> values, int count,\n        bool withRepetition = false)\n    {\n        return new Combinations<T>(values, count,\n            withRepetition ? GenerateOption.WithRepetition : GenerateOption.WithoutRepetition);\n    }\n\n    public static IEnumerable<IReadOnlyList<T>> GetPermutations<T>(this IEnumerable<T> values,\n        bool withRepetition = false, IComparer<T>? comparer = null)\n    {\n        return new Permutations<T>(values,\n            withRepetition ? GenerateOption.WithRepetition : GenerateOption.WithoutRepetition, comparer);\n    }\n\n    public static IEnumerable<IGrouping<TKey, T>> GroupByEquality<T, TKey>(this IEnumerable<T> source,\n        Func<T, TKey> keySelector,\n        IEqualityComparer<TKey> keyComparer,\n        Action<TKey, T>? keyAction = null, Action<T, TKey>? itemAction = null) where TKey : notnull\n    {\n        return GroupByEquality(source, keySelector, keyComparer.Equals, keyAction, itemAction);\n    }\n\n    public static IEnumerable<IGrouping<TKey, T>> GroupByEquality<T, TKey>(this IEnumerable<T> source,\n        Func<T, TKey> keySelector,\n        Func<TKey, TKey, bool> comparer,\n        Action<TKey, T>? keyAction = null, Action<T, TKey>? itemAction = null)\n    {\n        var groups = new List<Grouping<TKey, T>>();\n        foreach (var item in source)\n        {\n            var key = keySelector(item);\n            var group = groups.FirstOrDefault(x => comparer(x.Key, key));\n            if (group is null)\n            {\n                group = new Grouping<TKey, T>(key)\n                {\n                    item\n                };\n                groups.Add(group);\n            }\n            else\n            {\n                keyAction?.Invoke(group.Key, item);\n                group.Add(item);\n            }\n        }\n\n        if (itemAction != null)\n        {\n            foreach (var group in groups.Where(g => g.Count > 1))\n            {\n                foreach (var item in group)\n                    itemAction.Invoke(item, group.Key);\n            }\n        }\n\n        return groups;\n    }\n\n    private sealed class Grouping<TKey, T>(TKey key) : IGrouping<TKey, T>\n    {\n        private readonly List<T> _items = [];\n\n        public TKey Key { get; } = Guard.NotNull(key);\n\n        public void Add(T t) => _items.Add(t);\n\n        public int Count => _items.Count;\n\n        public IEnumerator<T> GetEnumerator()\n        {\n            return _items.GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/ExceptionExtension.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\n/// <summary>\n/// ExceptionExtension\n/// </summary>\npublic static class ExceptionExtension\n{\n    /// <summary>\n    /// get inner exception of AggregateException\n    /// </summary>\n    /// <param name=\"ex\">origin exception</param>\n    /// <param name=\"depth\">depth</param>\n    /// <returns>inner exception</returns>\n    [return: NotNullIfNotNull(nameof(ex))]\n    public static Exception? Unwrap(this Exception? ex, int depth = 16)\n    {\n        var exception = ex;\n        while (exception is AggregateException or TargetInvocationException\n               && exception.InnerException != null\n               && depth-- > 0)\n        {\n            exception = exception.InnerException;\n        }\n        return exception;\n    }\n\n    /// <summary>\n    /// Determines whether the provided <paramref name=\"exception\"/> should be considered fatal. An exception\n    /// is considered to be fatal if it, or any of the inner exceptions are one of the following type:\n    ///\n    /// - System.OutOfMemoryException\n    /// - System.InsufficientMemoryException\n    /// - System.ThreadAbortException\n    /// - System.AccessViolationException\n    /// - System.StackOverflowException\n    /// - System.TypeInitializationException\n    /// marked as Fatal.\n    /// </summary>\n    public static bool IsFatal(this Exception? exception)\n    {\n        var unwrappedException = exception.Unwrap(256);\n        return unwrappedException is OutOfMemoryException and not InsufficientMemoryException\n            or ThreadAbortException\n            or AccessViolationException\n            or StackOverflowException\n            or TypeInitializationException;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/ExpressionExtension.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing WeihanLi.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class ExpressionExtension\n{\n    // https://stackoverflow.com/questions/457316/combining-two-expressions-expressionfunct-bool/457328#457328\n\n    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)\n    {\n        var parameter = Expression.Parameter(typeof(T));\n\n        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);\n        var left = leftVisitor.Visit(expr1.Body);\n        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);\n        var right = rightVisitor.Visit(expr2.Body);\n\n        return Expression.Lambda<Func<T, bool>>(\n            Expression.OrElse(Guard.NotNull(left), Guard.NotNull(right)), parameter);\n    }\n\n    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,\n        Expression<Func<T, bool>> expr2)\n    {\n        var parameter = Expression.Parameter(typeof(T));\n\n        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);\n        var left = leftVisitor.Visit(expr1.Body);\n        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);\n        var right = rightVisitor.Visit(expr2.Body);\n\n        return Expression.Lambda<Func<T, bool>>(\n            Expression.AndAlso(Guard.NotNull(left), Guard.NotNull(right)), parameter);\n    }\n\n    public static Expression<Func<T, bool>> AndIf<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2, bool condition)\n    {\n        if (!condition)\n        {\n            return expr1;\n        }\n        var parameter = Expression.Parameter(typeof(T));\n\n        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);\n        var left = leftVisitor.Visit(expr1.Body);\n        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);\n        var right = rightVisitor.Visit(expr2.Body);\n\n        return Expression.Lambda<Func<T, bool>>(\n            Expression.AndAlso(Guard.NotNull(left), Guard.NotNull(right)), parameter);\n    }\n\n    private sealed class ReplaceExpressionVisitor(Expression oldValue, Expression newValue) : ExpressionVisitor\n    {\n        private readonly Expression _oldValue = oldValue;\n        private readonly Expression _newValue = newValue;\n\n        public override Expression? Visit(Expression? node)\n        {\n            if (node == _oldValue)\n                return _newValue;\n\n            return base.Visit(node);\n        }\n    }\n\n    public static MethodInfo GetMethod<T>(this Expression<T> expression)\n    {\n        Guard.NotNull(expression);\n\n        if (expression.Body is not MethodCallExpression methodCallExpression)\n        {\n            throw new InvalidCastException(\"Cannot be converted to MethodCallExpression\");\n        }\n        return methodCallExpression.Method;\n    }\n\n    public static MethodCallExpression GetMethodExpression<T>(this Expression<Action<T>> method)\n    {\n        if (method.Body.NodeType != ExpressionType.Call)\n            throw new ArgumentException(@\"Method call expected\", method.Body.ToString());\n        return (MethodCallExpression)method.Body;\n    }\n\n    public static MethodCallExpression GetMethodExpression<T>(this Expression<Func<T, object>> exp)\n    {\n        switch (exp.Body.NodeType)\n        {\n            case ExpressionType.Call:\n                return (MethodCallExpression)exp.Body;\n\n            case ExpressionType.Convert:\n                if (exp.Body is UnaryExpression { Operand: MethodCallExpression methodCallExpression })\n                {\n                    return methodCallExpression;\n                }\n                break;\n        }\n        throw new InvalidOperationException($\"Method expected: {exp.Body}\");\n    }\n\n    /// <summary>\n    /// GetMemberName\n    /// </summary>\n    /// <typeparam name=\"TEntity\">TEntity</typeparam>\n    /// <typeparam name=\"TMember\">TMember</typeparam>\n    /// <param name=\"memberExpression\">get member expression</param>\n    /// <returns></returns>\n    public static string\n        GetMemberName<TEntity, TMember>(this Expression<Func<TEntity, TMember>> memberExpression) =>\n        GetMemberInfo(memberExpression).Name;\n\n    /// <summary>\n    /// GetMemberInfo\n    /// </summary>\n    /// <typeparam name=\"TEntity\">TEntity</typeparam>\n    /// <typeparam name=\"TMember\">TMember</typeparam>\n    /// <param name=\"expression\">get member expression</param>\n    /// <returns></returns>\n    public static MemberInfo GetMemberInfo<TEntity, TMember>(this Expression<Func<TEntity, TMember>> expression)\n    {\n        if (expression.NodeType != ExpressionType.Lambda)\n        {\n            throw new ArgumentException(string.Format(Resource.propertyExpression_must_be_lambda_expression, nameof(expression)), nameof(expression));\n        }\n\n        var lambda = (LambdaExpression)expression;\n\n        var memberExpression = ExtractMemberExpression(lambda.Body) ?? throw new ArgumentException(string.Format(Resource.propertyExpression_must_be_lambda_expression, nameof(expression)), nameof(expression));\n        return memberExpression.Member;\n    }\n\n    /// <summary>\n    /// GetPropertyInfo\n    /// </summary>\n    /// <typeparam name=\"TEntity\"></typeparam>\n    /// <typeparam name=\"TProperty\"></typeparam>\n    /// <param name=\"expression\"></param>\n    /// <returns></returns>\n    public static PropertyInfo GetProperty<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TEntity, TProperty>(\n        this Expression<Func<TEntity, TProperty>> expression)\n    {\n        var member = GetMemberInfo(expression);\n        if (null == member)\n            throw new InvalidOperationException(\"no property found\");\n\n        if (member is PropertyInfo property)\n            return property;\n\n        return CacheUtil.GetTypeProperties(typeof(TEntity)).\n            First(p => p.Name.Equals(member.Name));\n    }\n\n    private static MemberExpression ExtractMemberExpression(Expression expression)\n    {\n        if (expression.NodeType == ExpressionType.MemberAccess)\n        {\n            return (MemberExpression)expression;\n        }\n\n        if (expression.NodeType == ExpressionType.Convert)\n        {\n            var operand = ((UnaryExpression)expression).Operand;\n            return ExtractMemberExpression(operand);\n        }\n\n        throw new InvalidOperationException(nameof(ExtractMemberExpression));\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/FuncExtension.cs",
    "content": "﻿using WeihanLi.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class FuncExtension\n{\n    public static Func<Task> WrapTask(this Action action)\n    {\n        return () =>\n        {\n            action.Invoke();\n            return Task.CompletedTask;\n        };\n    }\n\n    public static Func<T, Task> WrapTask<T>(this Action<T> action)\n    {\n        return (t) =>\n        {\n            action.Invoke(t);\n            return Task.CompletedTask;\n        };\n    }\n\n    public static Func<T1, T2, Task> WrapTask<T1, T2>(this Action<T1, T2> action)\n    {\n        return (t1, t2) =>\n        {\n            action.Invoke(t1, t2);\n            return Task.CompletedTask;\n        };\n    }\n\n    public static Func<T1, T2, T3, Task> WrapTask<T1, T2, T3>(this Action<T1, T2, T3> action)\n    {\n        return (t1, t2, t3) =>\n        {\n            action.Invoke(t1, t2, t3);\n            return Task.CompletedTask;\n        };\n    }\n\n    public static Func<T1, T2, T3, T4, Task> WrapTask<T1, T2, T3, T4>(this Action<T1, T2, T3, T4> action)\n    {\n        return (t1, t2, t3, t4) =>\n        {\n            action.Invoke(t1, t2, t3, t4);\n            return Task.CompletedTask;\n        };\n    }\n\n    public static Func<T, ValueTask> WrapValueTask<T>(this Action<T> action)\n    {\n        return (t) =>\n        {\n            action.Invoke(t);\n            return default;\n        };\n    }\n\n    public static Func<T1, T2, ValueTask> WrapValueTask<T1, T2>(this Action<T1, T2> action)\n    {\n        return (t1, t2) =>\n        {\n            action.Invoke(t1, t2);\n            return default;\n        };\n    }\n\n    public static Func<T1, T2, T3, ValueTask> WrapValueTask<T1, T2, T3>(this Action<T1, T2, T3> action)\n    {\n        return (t1, t2, t3) =>\n        {\n            action.Invoke(t1, t2, t3);\n            return default;\n        };\n    }\n\n    public static Func<T1, T2, T3, T4, ValueTask> WrapValueTask<T1, T2, T3, T4>(this Action<T1, T2, T3, T4> action)\n    {\n        return (t1, t2, t3, t4) =>\n        {\n            action.Invoke(t1, t2, t3, t4);\n            return default;\n        };\n    }\n\n    public static Func<CancellationToken, Task> WrapCancellation(this Func<Task> func) => _ => Guard.NotNull(func, nameof(func)).Invoke();\n\n    public static Func<T, CancellationToken, Task> WrapCancellation<T>(this Func<T, Task> func) => (t, _) => Guard.NotNull(func, nameof(func)).Invoke(t);\n\n    public static Func<T1, T2, CancellationToken, Task> WrapCancellation<T1, T2>(this Func<T1, T2, Task> func) => (t1, t2, _) => Guard.NotNull(func, nameof(func)).Invoke(t1, t2);\n\n    public static Func<T1, T2, T3, CancellationToken, Task> WrapCancellation<T1, T2, T3>(this Func<T1, T2, T3, Task> func) => (t1, t2, t3, _) => Guard.NotNull(func, nameof(func)).Invoke(t1, t2, t3);\n\n    public static Func<T1, T2, T3, T4, CancellationToken, Task> WrapCancellation<T1, T2, T3, T4>(this Func<T1, T2, T3, T4, Task> func) => (t1, t2, t3, t4, _) => Guard.NotNull(func, nameof(func)).Invoke(t1, t2, t3, t4);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/HttpClientExtension.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System.Net.Http.Headers;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Http;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class HttpClientExtension\n{\n    /// <summary>\n    /// HTTP Basic Authentication authorization header for RFC6749 client authentication\n    /// </summary>\n    /// <seealso cref=\"T:System.Net.Http.Headers.AuthenticationHeaderValue\" />\n    /// <remarks>\n    /// Initializes a new instance of the <see cref=\"T:System.Net.Http.BasicAuthenticationOAuthHeaderValue\" /> class.\n    /// </remarks>\n    /// <param name=\"userName\">Name of the user.</param>\n    /// <param name=\"password\">The password.</param>\n    private sealed class BasicAuthenticationHeaderValue(string userName, string password) : AuthenticationHeaderValue(\"Basic\", EncodeCredential(userName, password))\n    {\n        private static string EncodeCredential(string userName, string password)\n        {\n            Guard.NotNullOrWhiteSpace(userName);\n            return Convert.ToBase64String($\"{UrlEncode(userName)}:{UrlEncode(password)}\".GetBytes());\n        }\n\n        private static string UrlEncode(string value)\n        {\n            return string.IsNullOrEmpty(value)\n                ? string.Empty\n                : Uri.EscapeDataString(value).Replace(\"%20\", \"+\");\n        }\n    }\n\n    /// <summary>\n    /// Post object as json request body\n    /// </summary>\n    public static Task<HttpResponseMessage> PostJsonRequestAsync<T>(this HttpClient httpClient, string requestUrl, T parameter, Action<HttpRequestMessage>? requestAction = null,\n        CancellationToken cancellationToken = default)\n        => HttpJsonRequestAsync(httpClient, HttpMethod.Post, requestUrl, parameter, requestAction, cancellationToken);\n\n    /// <summary>\n    /// PutAsJsonAsync\n    /// </summary>\n    public static Task<HttpResponseMessage> PutJsonRequestAsync<T>(this HttpClient httpClient, string requestUrl, T parameter, Action<HttpRequestMessage>? requestAction = null,\n        CancellationToken cancellationToken = default)\n        => HttpJsonRequestAsync(httpClient, HttpMethod.Put, requestUrl, parameter, requestAction, cancellationToken);\n\n    /// <summary>\n    /// PostJson request body and get object from json response\n    /// </summary>\n    public static Task<TResponse?> PostJsonAsync<TRequest, TResponse>\n    (this HttpClient httpClient, string requestUrl,\n        TRequest request, Action<HttpRequestMessage>? requestAction = null,\n        Action<HttpResponseMessage>? responseAction = null,\n        CancellationToken cancellationToken = default)\n        => HttpJsonAsync<TRequest, TResponse>(httpClient, HttpMethod.Post, requestUrl, request, requestAction, responseAction,\n            cancellationToken);\n\n    /// <summary>\n    /// Put Json request body and get object from json response\n    /// </summary>\n    public static Task<TResponse?> PutJsonAsync<TRequest, TResponse>\n    (this HttpClient httpClient, string requestUrl,\n        TRequest request,\n        Action<HttpRequestMessage>? requestAction = null,\n        Action<HttpResponseMessage>? responseAction = null,\n        CancellationToken cancellationToken = default)\n        => HttpJsonAsync<TRequest, TResponse>(httpClient, HttpMethod.Put, requestUrl, request, requestAction, responseAction,\n            cancellationToken);\n\n    public static async Task<HttpResponseMessage> HttpJsonRequestAsync<TRequest>\n    (this HttpClient httpClient, HttpMethod httpMethod, string requestUrl,\n        TRequest request, Action<HttpRequestMessage>? requestAction = null,\n        CancellationToken cancellationToken = default)\n    {\n        Guard.NotNull(httpClient);\n        using var requestMessage = new HttpRequestMessage(httpMethod, requestUrl);\n        requestMessage.Content = JsonHttpContent.From(request);\n        requestAction?.Invoke(requestMessage);\n        return await httpClient.SendAsync(requestMessage, cancellationToken);\n    }\n\n    public static async Task<TResponse?> ReadJsonResponseAsync<TResponse>\n    (this HttpResponseMessage response, Action<HttpResponseMessage>? responseAction = null,\n        CancellationToken cancellationToken = default)\n    {\n        Guard.NotNull(response);\n        responseAction?.Invoke(response);\n#if NET\n        var responseText = await response.Content.ReadAsStringAsync(cancellationToken);\n#else\n        var responseText = await response.Content.ReadAsStringAsync();\n#endif\n        return JsonConvert.DeserializeObject<TResponse>(responseText);\n    }\n\n    public static async Task<TResponse?> HttpJsonAsync<TRequest, TResponse>\n    (this HttpClient httpClient, HttpMethod httpMethod, string requestUrl,\n        TRequest request, Action<HttpRequestMessage>? requestAction = null,\n        Action<HttpResponseMessage>? responseAction = null,\n        CancellationToken cancellationToken = default)\n    {\n        Guard.NotNull(httpClient);\n        using var requestMessage = new HttpRequestMessage(httpMethod, requestUrl);\n        requestMessage.Content = JsonHttpContent.From(request);\n        requestAction?.Invoke(requestMessage);\n        using var response = await httpClient.SendAsync(requestMessage, cancellationToken);\n        responseAction?.Invoke(response);\n#if NET\n        var responseText = await response.Content.ReadAsStringAsync(cancellationToken);\n#else\n        var responseText = await response.Content.ReadAsStringAsync();\n#endif\n        return JsonConvert.DeserializeObject<TResponse>(responseText);\n    }\n\n#if NET\n    /// <summary>\n    /// PatchAsJsonAsync\n    /// </summary>\n    public static Task<HttpResponseMessage> PatchJsonRequestAsync<T>(this HttpClient httpClient, string requestUrl, T parameter, Action<HttpRequestMessage>? requestAction = null,\n        CancellationToken cancellationToken = default)\n         => HttpJsonRequestAsync(httpClient, HttpMethod.Patch, requestUrl, parameter, requestAction, cancellationToken);\n\n    /// <summary>\n    /// Patch Json request body and get object from json response\n    /// </summary>\n    public static Task<TResponse?> PatchJsonAsync<TRequest, TResponse>\n    (this HttpClient httpClient, string requestUrl,\n        TRequest request, Action<HttpRequestMessage>? requestAction = null,\n        Action<HttpResponseMessage>? responseAction = null,\n        CancellationToken cancellationToken = default)\n        => HttpJsonAsync<TRequest, TResponse>(httpClient, HttpMethod.Patch, requestUrl, request, requestAction, responseAction,\n            cancellationToken);\n#endif\n\n    /// <summary>\n    /// PostAsFormAsync\n    /// </summary>\n    public static Task<HttpResponseMessage> PostAsFormAsync(this HttpClient httpClient, string requestUri, IEnumerable<KeyValuePair<string, string>> paramDic, CancellationToken cancellationToken = default)\n        => httpClient.PostAsync(requestUri, new FormUrlEncodedContent(paramDic), cancellationToken);\n\n    /// <summary>\n    /// PutAsFormAsync\n    /// </summary>\n    public static Task<HttpResponseMessage> PutAsFormAsync(this HttpClient httpClient, string requestUri, IEnumerable<KeyValuePair<string, string>> paramDic, CancellationToken cancellationToken = default)\n        => httpClient.PutAsync(requestUri, new FormUrlEncodedContent(paramDic), cancellationToken);\n\n    /// <summary>\n    /// HttpClient Post a file\n    /// </summary>\n    /// <param name=\"httpClient\">httpClient</param>\n    /// <param name=\"requestUrl\">requestUrl</param>\n    /// <param name=\"filePath\">filePath</param>\n    /// <param name=\"fileKey\">fileKey,default is \"file\"</param>\n    /// <param name=\"formFields\">formFields,default is null</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>response message</returns>\n    public static Task<HttpResponseMessage> PostFileAsync(this HttpClient httpClient,\n        string requestUrl,\n        string filePath,\n        string fileKey = \"file\",\n        IEnumerable<KeyValuePair<string, string>>? formFields = null, CancellationToken cancellationToken = default)\n    {\n        var content = new MultipartFormDataContent($\"form--{DateTime.UtcNow.Ticks:X}\");\n\n        if (formFields != null)\n        {\n            foreach (var kv in formFields)\n            {\n                content.Add(new StringContent(kv.Value), kv.Key);\n            }\n        }\n\n        content.Add(new StreamContent(File.OpenRead(filePath)), fileKey, Path.GetFileName(filePath));\n\n        return httpClient.PostAsync(requestUrl, content, cancellationToken);\n    }\n\n    /// <summary>\n    /// HttpClient Post a file\n    /// </summary>\n    /// <param name=\"httpClient\">httpClient</param>\n    /// <param name=\"requestUrl\">requestUrl</param>\n    /// <param name=\"file\">fileStream</param>\n    /// <param name=\"fileName\">fileName</param>\n    /// <param name=\"fileKey\">fileKey,default is \"file\"</param>\n    /// <param name=\"formFields\">formFields,default is null</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns></returns>\n    public static async Task<HttpResponseMessage> PostFileAsync(this HttpClient httpClient,\n        string requestUrl,\n        Stream? file,\n        string fileName,\n        string fileKey = \"file\",\n        IEnumerable<KeyValuePair<string, string>>? formFields = null, CancellationToken cancellationToken = default)\n    {\n        if (file == null)\n        {\n            return await httpClient.PostAsFormAsync(requestUrl, formFields ?? Array.Empty<KeyValuePair<string, string>>(), cancellationToken);\n        }\n\n        var content = new MultipartFormDataContent($\"form--{DateTime.UtcNow.Ticks:X}\");\n\n        if (formFields != null)\n        {\n            foreach (var kv in formFields)\n            {\n                content.Add(new StringContent(kv.Value), kv.Key);\n            }\n        }\n\n        content.Add(new StreamContent(file), fileKey, fileName);\n\n        return await httpClient.PostAsync(requestUrl, content, cancellationToken);\n    }\n\n    public static Task<HttpResponseMessage> PostFileAsync(this HttpClient\n            client, string requestUrl, ICollection<string> filePaths,\n        IEnumerable<KeyValuePair<string, string>>? formFields = null, CancellationToken cancellationToken = default) =>\n        client.PostFileAsync(requestUrl, filePaths.Select(p =>\n                new KeyValuePair<string, Stream>(Path.GetFileName(p), File.OpenRead(p))), formFields, cancellationToken);\n\n    /// <summary>\n    /// HttpClient Post files. <see href=\"https://stackoverflow.com/questions/18059588/httpclient-multipart-form-post-in-c-sharp\">See here</see>.\n    /// </summary>\n    /// <param name=\"httpClient\">The HTTP client.</param>\n    /// <param name=\"requestUri\">The request URI.</param>\n    /// <param name=\"files\">The files. Key: File name, value: file read stream.</param>\n    /// <param name=\"formFields\">The form.</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns></returns>\n    public static async Task<HttpResponseMessage> PostFileAsync(this HttpClient httpClient,\n        string requestUri,\n        IEnumerable<KeyValuePair<string, Stream>>? files,\n        IEnumerable<KeyValuePair<string, string>>? formFields = null,\n        CancellationToken cancellationToken = default)\n    {\n        if (files is null)\n        {\n            return await httpClient.PostAsFormAsync(requestUri, formFields ?? Array.Empty<KeyValuePair<string, string>>(), cancellationToken);\n        }\n\n        var content = new MultipartFormDataContent($\"form--{DateTime.UtcNow.Ticks:X}\");\n\n        if (formFields != null)\n        {\n            foreach (var kv in formFields)\n            {\n                content.Add(new StringContent(kv.Value), kv.Key);\n            }\n        }\n\n        foreach (var file in files)\n        {\n            content.Add(new StreamContent(file.Value), Path.GetFileNameWithoutExtension(file.Key), Path.GetFileName(file.Key));\n        }\n\n        return await httpClient.PostAsync(requestUri, content, cancellationToken);\n    }\n\n    /// <summary>\n    /// Sets the basic authentication.\n    /// </summary>\n    /// <param name=\"client\">The client.</param>\n    /// <param name=\"userName\">Name of the user.</param>\n    /// <param name=\"password\">The password.</param>\n    public static void SetBasicAuthentication(this HttpClient client, string userName, string password) =>\n        client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(userName, password);\n\n    /// <summary>\n    /// Sets a basic authentication header.\n    /// </summary>\n    /// <param name=\"request\">The HTTP request message.</param>\n    /// <param name=\"userName\">Name of the user.</param>\n    /// <param name=\"password\">The password.</param>\n    public static void SetBasicAuthentication(this HttpRequestMessage request, string userName, string password) => request.Headers.Authorization = new BasicAuthenticationHeaderValue(userName, password);\n\n    /// <summary>\n    /// Sets a basic authentication header for RFC6749 client authentication.\n    /// </summary>\n    /// <param name=\"request\">The HTTP request message.</param>\n    /// <param name=\"userName\">Name of the user.</param>\n    /// <param name=\"password\">The password.</param>\n    public static void SetBasicAuthenticationOAuth(this HttpRequestMessage request, string userName, string password) => request.Headers.Authorization = new BasicAuthenticationHeaderValue(userName, password);\n\n    /// <summary>\n    /// Sets the token.\n    /// </summary>\n    /// <param name=\"client\">The client.</param>\n    /// <param name=\"scheme\">The scheme.</param>\n    /// <param name=\"token\">The token.</param>\n    public static void SetToken(this HttpClient client, string scheme, string token) =>\n        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(scheme, token);\n\n    /// <summary>\n    /// Sets an authorization header with a given scheme and value.\n    /// </summary>\n    /// <param name=\"request\">The HTTP request message.</param>\n    /// <param name=\"scheme\">The scheme.</param>\n    /// <param name=\"token\">The token.</param>\n    public static void SetToken(this HttpRequestMessage request, string scheme, string token) => request.Headers.Authorization = new AuthenticationHeaderValue(scheme, token);\n\n    /// <summary>\n    /// Sets the bearer token.\n    /// </summary>\n    /// <param name=\"client\">The client.</param>\n    /// <param name=\"token\">The token.</param>\n    public static void SetBearerToken(this HttpClient client, string token) => client.SetToken(\"Bearer\", token);\n\n    /// <summary>\n    /// Sets an authorization header with a bearer token.\n    /// </summary>\n    /// <param name=\"request\">The HTTP request message.</param>\n    /// <param name=\"token\">The token.</param>\n    public static void SetBearerToken(this HttpRequestMessage request, string token) => request.SetToken(\"Bearer\", token);\n\n    /// <summary>\n    /// Try to add a header to the request message, would add to content header if the header belongs to content header \n    /// </summary>\n    /// <param name=\"requestMessage\">request message to add</param>\n    /// <param name=\"headerName\">header name</param>\n    /// <param name=\"headerValue\">header value</param>\n    /// <returns>the request message</returns>\n    public static HttpRequestMessage TryAddHeader(this HttpRequestMessage requestMessage,\n        string headerName, string headerValue)\n    {\n        Guard.NotNull(requestMessage);\n        Guard.NotNullOrEmpty(headerName);\n        if (HttpHelper.IsWellKnownContentHeader(headerName))\n        {\n            requestMessage.Content?.Headers.Remove(headerName);\n            requestMessage.Content?.Headers.TryAddWithoutValidation(headerName, headerValue);\n        }\n        else\n        {\n            requestMessage.Headers.TryAddWithoutValidation(headerName, headerValue);\n        }\n        return requestMessage;\n    }\n\n    /// <summary>\n    /// Try to add a header to the request message when not exists, would add to content header if the header belongs to content header \n    /// </summary>\n    /// <param name=\"requestMessage\">request message to add</param>\n    /// <param name=\"headerName\">header name</param>\n    /// <param name=\"headerValue\">header value</param>\n    /// <returns>the request message</returns>\n    public static HttpRequestMessage TryAddHeaderIfNotExists(this HttpRequestMessage requestMessage,\n        string headerName, string headerValue)\n    {\n        Guard.NotNull(requestMessage);\n        Guard.NotNullOrEmpty(headerName);\n        if (HttpHelper.IsWellKnownContentHeader(headerName))\n        {\n            if (requestMessage.Content is not null && !requestMessage.Content.Headers.Contains(headerName))\n            {\n                requestMessage.Content.Headers.TryAddWithoutValidation(headerName, headerValue);\n            }\n        }\n        else\n        {\n            if (!requestMessage.Headers.Contains(headerName))\n            {\n                requestMessage.Headers.TryAddWithoutValidation(headerName, headerValue);\n            }\n        }\n        return requestMessage;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/HttpRequesterExtension.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Net;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Http;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class HttpRequesterExtension\n{\n    public static IHttpRequester WithUrl(this IHttpRequester httpRequester, string url, IEnumerable<KeyValuePair<string, string>> queryParams)\n    {\n        var requestUrl = url.IndexOf(\"?\", StringComparison.OrdinalIgnoreCase) > 0\n            ? $\"{url}?{queryParams.ToQueryString()}\"\n            : $\"{url}&{queryParams.ToQueryString()}\";\n        return httpRequester.WithUrl($\"{requestUrl}\");\n    }\n\n    public static IHttpRequester AjaxRequest(this IHttpRequester httpRequester)\n    {\n        return httpRequester.WithHeaders(new[]\n        {\n                new KeyValuePair<string, string?>(\"X-Requested-With\", \"XMLHttpRequest\")\n            });\n    }\n\n    public static IHttpRequester WithCookie(this IHttpRequester httpRequester, string cookieName, string cookieValue)\n    {\n        return httpRequester.WithCookie(new Cookie(cookieName, cookieValue));\n    }\n\n    public static IHttpRequester WithCookie(this IHttpRequester httpRequester, Cookie cookie)\n    {\n        return httpRequester.WithCookie(null, cookie);\n    }\n\n    public static IHttpRequester WithCookie(this IHttpRequester httpRequester, CookieCollection cookies)\n    {\n        return httpRequester.WithCookie(null, cookies);\n    }\n\n    public static IHttpRequester WithProxy(this IHttpRequester httpRequester, string url)\n    {\n        return httpRequester.WithProxy(new WebProxy(new Uri(url)));\n    }\n\n    public static IHttpRequester WithProxy(this IHttpRequester httpRequester, string url, string userName, string password)\n    {\n        return httpRequester.WithProxy(new WebProxy(new Uri(url))\n        {\n            Credentials = new NetworkCredential(userName, password)\n        });\n    }\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public static IHttpRequester WithXmlParameter<TEntity>(this IHttpRequester httpRequester, TEntity entity)\n    {\n        return httpRequester.WithParameters(XmlDataSerializer.Instance.Value.Serialize(entity), \"application/xml;charset=UTF-8\");\n    }\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public static IHttpRequester WithJsonParameter<TEntity>(this IHttpRequester httpRequester, TEntity entity)\n    {\n        return httpRequester.WithParameters(entity.ToJson().GetBytes(), HttpHelper.ApplicationJsonContentType);\n    }\n\n    public static IHttpRequester WithFormParams(this IHttpRequester httpRequester,\n        IEnumerable<KeyValuePair<string, string>> formParams)\n    {\n        return httpRequester.WithParameters(formParams.ToQueryString().GetBytes(), HttpHelper.FormDataContentType);\n    }\n\n    public static IHttpRequester WithFile(this IHttpRequester httpRequester, string filePath, string fileKey = \"file\",\n        IEnumerable<KeyValuePair<string, string>>? formFields = null)\n        => httpRequester.WithFile(Path.GetFileName(filePath), File.ReadAllBytes(filePath), fileKey, formFields);\n\n    public static IHttpRequester WithFiles(this IHttpRequester httpRequester, IEnumerable<string> filePaths, IEnumerable<KeyValuePair<string, string>>? formFields = null)\n        => httpRequester.WithFiles(\n            filePaths.Select(f => new KeyValuePair<string, byte[]>(\n                Path.GetFileName(f),\n                File.ReadAllBytes(f))),\n            formFields);\n\n    public static string Execute(this IHttpRequester httpRequester) => httpRequester.ExecuteBytes().GetString();\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T? Execute<T>(this IHttpRequester httpRequester, T? defaultVal = default) => httpRequester.ExecuteBytes().GetString().ToOrDefault(defaultVal);\n\n    public static Task<string> ExecuteAsync(this IHttpRequester httpRequester)\n    {\n        return httpRequester.ExecuteBytesAsync().ContinueWith(r => r.Result.GetString());\n    }\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static Task<T?> ExecuteAsync<T>(this IHttpRequester httpRequester, T? defaultVal = default)\n    {\n        return httpRequester.ExecuteBytesAsync().ContinueWith(r => r.Result.GetString().ToOrDefault(defaultVal));\n    }\n\n    public static TEntity ExecuteForJson<TEntity>(this IHttpRequester httpRequester)\n    {\n        return httpRequester.Execute().JsonToObject<TEntity>();\n    }\n\n    public static Task<TEntity> ExecuteForJsonAsync<TEntity>(this IHttpRequester httpRequester)\n    {\n        return httpRequester.ExecuteAsync().ContinueWith(r => r.Result.JsonToObject<TEntity>());\n    }\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public static TEntity ExecuteForXml<TEntity>(this IHttpRequester httpRequester)\n    {\n        return XmlDataSerializer.Instance.Value.Deserialize<TEntity>(httpRequester.ExecuteBytes());\n    }\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public static Task<TEntity> ExecuteForXmlAsync<TEntity>(this IHttpRequester httpRequester)\n    {\n        return httpRequester.ExecuteBytesAsync().ContinueWith(r => XmlDataSerializer.Instance.Value.Deserialize<TEntity>(r.Result));\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/ILGeneratorExtensions.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing System.Reflection.Emit;\nusing System.Runtime.InteropServices;\nusing WeihanLi.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\n// ReSharper disable once InconsistentNaming\npublic static class ILGeneratorExtensions\n{\n    public static readonly MethodInfo GetMethodFromHandle = ExpressionExtension.GetMethod<Func<RuntimeMethodHandle, RuntimeTypeHandle, MethodBase>>((h1, h2) => MethodBase.GetMethodFromHandle(h1, h2)!);\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static void EmitMethod(this ILGenerator ilGenerator, MethodInfo method)\n    {\n        Guard.NotNull(ilGenerator);\n        Guard.NotNull(method);\n\n        EmitMethod(ilGenerator, method, method.DeclaringType!);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static void EmitMethod(this ILGenerator ilGenerator, MethodInfo method, Type declaringType)\n    {\n        Guard.NotNull(ilGenerator);\n        Guard.NotNull(method);\n        Guard.NotNull(declaringType);\n\n        ilGenerator.Emit(OpCodes.Ldtoken, method);\n        ilGenerator.Emit(OpCodes.Ldtoken, declaringType);\n        ilGenerator.Emit(OpCodes.Call, GetMethodFromHandle);\n        ilGenerator.EmitConvertToType(typeof(MethodBase), typeof(MethodInfo));\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static void EmitConvertToObject(this ILGenerator ilGenerator,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type typeFrom)\n    {\n        Guard.NotNull(ilGenerator);\n        Guard.NotNull(typeFrom);\n\n        if (typeFrom.IsGenericParameter)\n        {\n            ilGenerator.Emit(OpCodes.Box, typeFrom);\n        }\n        else\n        {\n            ilGenerator.EmitConvertToType(typeFrom, typeof(object));\n        }\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static void EmitConvertFromObject(this ILGenerator ilGenerator,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] Type typeTo)\n    {\n        Guard.NotNull(ilGenerator);\n        Guard.NotNull(typeTo);\n\n        if (typeTo.IsGenericParameter)\n        {\n            ilGenerator.Emit(OpCodes.Unbox_Any, typeTo);\n        }\n        else\n        {\n            ilGenerator.EmitConvertToType(typeof(object), typeTo);\n        }\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static void EmitConvertToType(this ILGenerator ilGenerator,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type typeFrom,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] Type typeTo,\n        bool isChecked = true)\n    {\n        Guard.NotNull(ilGenerator);\n        Guard.NotNull(typeFrom);\n        Guard.NotNull(typeTo);\n\n        var nnExprType = typeFrom.Unwrap();\n        var nnType = typeTo.Unwrap();\n\n        if (nnType == nnExprType || nnType.IsEquivalentTo(nnExprType))\n        {\n            return;\n        }\n\n        if (typeFrom.IsInterface || // interface cast\n          typeTo.IsInterface ||\n           typeFrom == typeof(object) || // boxing cast\n           typeTo == typeof(object) ||\n           typeFrom == typeof(Enum) ||\n           typeFrom == typeof(ValueType) ||\n           TypeInfoUtils.IsLegalExplicitVariantDelegateConversion(typeFrom, typeTo))\n        {\n            ilGenerator.EmitCastToType(typeFrom, typeTo);\n        }\n        else if (typeFrom.IsNullableType() || typeTo.IsNullableType())\n        {\n            ilGenerator.EmitNullableConversion(typeFrom, typeTo, isChecked);\n        }\n        else if (!(typeFrom.IsConvertible() && typeTo.IsConvertible()) // primitive runtime conversion\n                 &&\n                 (nnExprType.GetTypeInfo().IsAssignableFrom(nnType) || // down cast\n                 nnType.GetTypeInfo().IsAssignableFrom(nnExprType))) // up cast\n        {\n            ilGenerator.EmitCastToType(typeFrom, typeTo);\n        }\n        else if (typeFrom.IsArray && typeTo.IsArray)\n        {\n            // See DevDiv Bugs #94657.\n            ilGenerator.EmitCastToType(typeFrom, typeTo);\n        }\n        else\n        {\n            ilGenerator.EmitNumericConversion(typeFrom, typeTo, isChecked);\n        }\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    private static void EmitNullableConversion(this ILGenerator ilGenerator,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type typeFrom,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] Type typeTo,\n        bool isChecked)\n    {\n        var isTypeFromNullable = typeFrom.IsNullableType();\n        var isTypeToNullable = typeTo.IsNullableType();\n        if (isTypeFromNullable && isTypeToNullable)\n            ilGenerator.EmitNullableToNullableConversion(typeFrom, typeTo, isChecked);\n        else if (isTypeFromNullable)\n            ilGenerator.EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked);\n        else\n            ilGenerator.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    private static void EmitNullableToNullableConversion(this ILGenerator ilGenerator,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type typeFrom,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] Type typeTo,\n        bool isChecked)\n    {\n        var locFrom = ilGenerator.DeclareLocal(typeFrom);\n        ilGenerator.Emit(OpCodes.Stloc, locFrom);\n        var locTo = ilGenerator.DeclareLocal(typeTo);\n        // test for null\n        ilGenerator.Emit(OpCodes.Ldloca, locFrom);\n        ilGenerator.EmitHasValue(typeFrom);\n        var labIfNull = ilGenerator.DefineLabel();\n        ilGenerator.Emit(OpCodes.Brfalse_S, labIfNull);\n        ilGenerator.Emit(OpCodes.Ldloca, locFrom);\n        ilGenerator.EmitGetValueOrDefault(typeFrom);\n        var nnTypeFrom = typeFrom.GetNonNullableType();\n        var nnTypeTo = typeTo.GetNonNullableType();\n        ilGenerator.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked);\n        // construct result type\n        var ci = typeTo.GetConstructor([nnTypeTo]);\n        ilGenerator.Emit(OpCodes.Newobj, ci!);\n        ilGenerator.Emit(OpCodes.Stloc, locTo);\n        var labEnd = ilGenerator.DefineLabel();\n        ilGenerator.Emit(OpCodes.Br_S, labEnd);\n        // if null then create a default one\n        ilGenerator.MarkLabel(labIfNull);\n        ilGenerator.Emit(OpCodes.Ldloca, locTo);\n        ilGenerator.Emit(OpCodes.Initobj, typeTo);\n        ilGenerator.MarkLabel(labEnd);\n        ilGenerator.Emit(OpCodes.Ldloc, locTo);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    private static void EmitNullableToNonNullableConversion(this ILGenerator ilGenerator,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type typeFrom,\n        Type typeTo, bool isChecked)\n    {\n        if (typeTo.IsValueType)\n            ilGenerator.EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked);\n        else\n            ilGenerator.EmitNullableToReferenceConversion(typeFrom);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    private static void EmitNullableToNonNullableStructConversion(this ILGenerator ilGenerator,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type typeFrom,\n        Type typeTo, bool isChecked)\n    {\n        var locFrom = ilGenerator.DeclareLocal(typeFrom);\n        ilGenerator.Emit(OpCodes.Stloc, locFrom);\n        ilGenerator.Emit(OpCodes.Ldloca, locFrom);\n        ilGenerator.EmitGetValue(typeFrom);\n        var nnTypeFrom = typeFrom.GetNonNullableType();\n        ilGenerator.EmitConvertToType(nnTypeFrom, typeTo, isChecked);\n    }\n\n    private static void EmitNullableToReferenceConversion(this ILGenerator ilGenerator, Type typeFrom)\n    {\n        // We've got a conversion from nullable to Object, ValueType, Enum, etc.  Just box it so that\n        // we get the nullable semantics.\n        ilGenerator.Emit(OpCodes.Box, typeFrom);\n    }\n\n    [RequiresDynamicCode(\"Defining a dynamic assembly requires dynamic code.\")]\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    private static void EmitNonNullableToNullableConversion(this ILGenerator ilGenerator,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type typeFrom,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] Type typeTo,\n        bool isChecked)\n    {\n        var locTo = ilGenerator.DeclareLocal(typeTo);\n        var nnTypeTo = typeTo.Unwrap();\n        ilGenerator.EmitConvertToType(typeFrom, nnTypeTo, isChecked);\n        var ci = Guard.NotNull(typeTo.GetConstructor([nnTypeTo])!, \"constructor\");\n        ilGenerator.Emit(OpCodes.Newobj, ci);\n        ilGenerator.Emit(OpCodes.Stloc, locTo);\n        ilGenerator.Emit(OpCodes.Ldloc, locTo);\n    }\n\n    private static void EmitNumericConversion(this ILGenerator ilGenerator, Type typeFrom, Type typeTo, bool isChecked)\n    {\n        var isFromUnsigned = TypeInfoUtils.IsUnsigned(typeFrom);\n        var isFromFloatingPoint = TypeInfoUtils.IsFloatingPoint(typeFrom);\n        if (typeTo == typeof(float))\n        {\n            if (isFromUnsigned)\n                ilGenerator.Emit(OpCodes.Conv_R_Un);\n            ilGenerator.Emit(OpCodes.Conv_R4);\n        }\n        else if (typeTo == typeof(double))\n        {\n            if (isFromUnsigned)\n                ilGenerator.Emit(OpCodes.Conv_R_Un);\n            ilGenerator.Emit(OpCodes.Conv_R8);\n        }\n        else\n        {\n            var tc = typeTo.GetTypeCode();\n            if (isChecked)\n            {\n                // Overflow checking needs to know if the source value on the IL stack is unsigned or not.\n                if (isFromUnsigned)\n                {\n                    switch (tc)\n                    {\n                        case TypeCode.SByte:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_I1_Un);\n                            break;\n\n                        case TypeCode.Int16:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_I2_Un);\n                            break;\n\n                        case TypeCode.Int32:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_I4_Un);\n                            break;\n\n                        case TypeCode.Int64:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_I8_Un);\n                            break;\n\n                        case TypeCode.Byte:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_U1_Un);\n                            break;\n\n                        case TypeCode.UInt16:\n                        case TypeCode.Char:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_U2_Un);\n                            break;\n\n                        case TypeCode.UInt32:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_U4_Un);\n                            break;\n\n                        case TypeCode.UInt64:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_U8_Un);\n                            break;\n\n                        default:\n                            throw new InvalidCastException();\n                    }\n                }\n                else\n                {\n                    switch (tc)\n                    {\n                        case TypeCode.SByte:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_I1);\n                            break;\n\n                        case TypeCode.Int16:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_I2);\n                            break;\n\n                        case TypeCode.Int32:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_I4);\n                            break;\n\n                        case TypeCode.Int64:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_I8);\n                            break;\n\n                        case TypeCode.Byte:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_U1);\n                            break;\n\n                        case TypeCode.UInt16:\n                        case TypeCode.Char:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_U2);\n                            break;\n\n                        case TypeCode.UInt32:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_U4);\n                            break;\n\n                        case TypeCode.UInt64:\n                            ilGenerator.Emit(OpCodes.Conv_Ovf_U8);\n                            break;\n\n                        default:\n                            throw new InvalidCastException();\n                    }\n                }\n            }\n            else\n            {\n                switch (tc)\n                {\n                    case TypeCode.SByte:\n                        ilGenerator.Emit(OpCodes.Conv_I1);\n                        break;\n\n                    case TypeCode.Byte:\n                        ilGenerator.Emit(OpCodes.Conv_U1);\n                        break;\n\n                    case TypeCode.Int16:\n                        ilGenerator.Emit(OpCodes.Conv_I2);\n                        break;\n\n                    case TypeCode.UInt16:\n                    case TypeCode.Char:\n                        ilGenerator.Emit(OpCodes.Conv_U2);\n                        break;\n\n                    case TypeCode.Int32:\n                        ilGenerator.Emit(OpCodes.Conv_I4);\n                        break;\n\n                    case TypeCode.UInt32:\n                        ilGenerator.Emit(OpCodes.Conv_U4);\n                        break;\n\n                    case TypeCode.Int64:\n                        if (isFromUnsigned)\n                        {\n                            ilGenerator.Emit(OpCodes.Conv_U8);\n                        }\n                        else\n                        {\n                            ilGenerator.Emit(OpCodes.Conv_I8);\n                        }\n                        break;\n\n                    case TypeCode.UInt64:\n                        if (isFromUnsigned || isFromFloatingPoint)\n                        {\n                            ilGenerator.Emit(OpCodes.Conv_U8);\n                        }\n                        else\n                        {\n                            ilGenerator.Emit(OpCodes.Conv_I8);\n                        }\n                        break;\n\n                    default:\n                        throw new InvalidCastException();\n                }\n            }\n        }\n    }\n\n    public static void EmitCastToType(this ILGenerator ilGenerator, Type typeFrom, Type typeTo)\n    {\n        Guard.NotNull(ilGenerator);\n\n        if (!typeFrom.IsValueType && typeTo.IsValueType)\n        {\n            ilGenerator.Emit(OpCodes.Unbox_Any, typeTo);\n        }\n        else if (typeFrom.IsValueType && !typeTo.IsValueType)\n        {\n            ilGenerator.Emit(OpCodes.Box, typeFrom);\n            if (typeTo != typeof(object))\n            {\n                ilGenerator.Emit(OpCodes.Castclass, typeTo);\n            }\n        }\n        else if (!typeFrom.IsValueType && !typeTo.IsValueType)\n        {\n            ilGenerator.Emit(OpCodes.Castclass, typeTo);\n        }\n        else\n        {\n            throw new InvalidCastException($\"Can not cast {typeFrom} to {typeTo}.\");\n        }\n    }\n\n    public static LocalBuilder? DeclareReturnValue(this ILGenerator il, Type returnType)\n    {\n        if (returnType != typeof(void))\n        {\n            return il.DeclareLocal(returnType);\n        }\n\n        return null;\n    }\n\n    public static void Return(this ILGenerator il, MethodInfo method, LocalBuilder local)\n    {\n        if (method.ReturnType != typeof(void))\n        {\n            il.Emit(OpCodes.Stloc, local);\n            il.Emit(OpCodes.Ldloc, local);\n        }\n\n        il.Emit(OpCodes.Ret);\n    }\n\n    public static void Call(this ILGenerator il, MethodInfo method)\n    {\n        il.Emit(OpCodes.Call, method);\n    }\n\n    public static void Call(this ILGenerator il, ConstructorInfo constructor)\n    {\n        il.Emit(OpCodes.Call, constructor);\n    }\n\n    public static void New(this ILGenerator il, ConstructorInfo constructor)\n    {\n        il.Emit(OpCodes.Newobj, constructor);\n    }\n\n    // https://stackoverflow.com/a/48149905/5519747\n    public static void LoadObj(this ILGenerator il, object? obj)\n    {\n        if (obj is null)\n        {\n            il.EmitNull();\n            return;\n        }\n\n        var type = obj.GetType();\n\n        var gch = GCHandle.Alloc(obj);\n        var ptr = GCHandle.ToIntPtr(gch);\n\n        if (IntPtr.Size == 4)\n            il.Emit(OpCodes.Ldc_I4, ptr.ToInt32());\n        else\n            il.Emit(OpCodes.Ldc_I8, ptr.ToInt64());\n\n        il.Emit(OpCodes.Ldobj, type);\n    }\n\n    public static void EmitThis(this ILGenerator ilGenerator)\n    {\n        Guard.NotNull(ilGenerator);\n\n        ilGenerator.Emit(OpCodes.Ldarg_0);\n    }\n\n    public static void EmitLoadArg(this ILGenerator ilGenerator, int index)\n    {\n        Guard.NotNull(ilGenerator);\n\n        switch (index)\n        {\n            case 0:\n                ilGenerator.Emit(OpCodes.Ldarg_0);\n                break;\n\n            case 1:\n                ilGenerator.Emit(OpCodes.Ldarg_1);\n                break;\n\n            case 2:\n                ilGenerator.Emit(OpCodes.Ldarg_2);\n                break;\n\n            case 3:\n                ilGenerator.Emit(OpCodes.Ldarg_3);\n                break;\n\n            default:\n                if (index <= byte.MaxValue)\n                {\n                    ilGenerator.Emit(OpCodes.Ldarg_S, (byte)index);\n                }\n                else\n                {\n                    ilGenerator.Emit(OpCodes.Ldarg, index);\n                }\n                break;\n        }\n    }\n\n    public static void EmitNull(this ILGenerator ilGenerator)\n    {\n        ilGenerator.Emit(OpCodes.Ldnull);\n    }\n\n    public static void EmitString(this ILGenerator ilGenerator, string value)\n    {\n        ilGenerator.Emit(OpCodes.Ldstr, value);\n    }\n\n    public static void EmitBoolean(this ILGenerator ilGenerator, bool value)\n    {\n        if (value)\n        {\n            ilGenerator.Emit(OpCodes.Ldc_I4_1);\n        }\n        else\n        {\n            ilGenerator.Emit(OpCodes.Ldc_I4_0);\n        }\n    }\n\n    public static void EmitHasValue(this ILGenerator ilGenerator, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type nullableType)\n    {\n        Guard.NotNull(ilGenerator);\n\n        var mi = nullableType.GetTypeInfo().GetMethod(\"get_HasValue\", BindingFlags.Instance | BindingFlags.Public)!;\n        ilGenerator.Emit(OpCodes.Call, mi);\n    }\n\n    public static void EmitGetValueOrDefault(this ILGenerator ilGenerator, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type nullableType)\n    {\n        var mi = nullableType.GetTypeInfo().GetMethod(\"GetValueOrDefault\", Type.EmptyTypes)!;\n        ilGenerator.Emit(OpCodes.Call, mi);\n    }\n\n    public static void EmitGetValue(this ILGenerator ilGenerator, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type nullableType)\n    {\n        var mi = nullableType.GetTypeInfo().GetMethod(\"get_Value\", BindingFlags.Instance | BindingFlags.Public)!;\n        ilGenerator.Emit(OpCodes.Call, mi);\n    }\n\n    public static void EmitDefaultValue(this ILGenerator ilGenerator, Type type)\n    {\n        Guard.NotNull(ilGenerator);\n        Guard.NotNull(type);\n\n        switch (Type.GetTypeCode(type))\n        {\n            case TypeCode.Object:\n            case TypeCode.DateTime:\n                if (type.IsValueType)\n                {\n                    // Type.GetTypeCode on an enum returns the underlying\n                    // integer TypeCode, so we won't get here.\n                    // This is the IL for default(T) if T is a generic type\n                    // parameter, so it should work for any type. It's also\n                    // the standard pattern for structs.\n                    var lb = ilGenerator.DeclareLocal(type);\n                    ilGenerator.Emit(OpCodes.Ldloca, lb);\n                    ilGenerator.Emit(OpCodes.Initobj, type);\n                    ilGenerator.Emit(OpCodes.Ldloc, lb);\n                }\n                else\n                {\n                    ilGenerator.Emit(OpCodes.Ldnull);\n                }\n                break;\n\n            case TypeCode.Empty:\n            case TypeCode.String:\n                ilGenerator.Emit(OpCodes.Ldnull);\n                break;\n\n            case TypeCode.Boolean:\n            case TypeCode.Char:\n            case TypeCode.SByte:\n            case TypeCode.Byte:\n            case TypeCode.Int16:\n            case TypeCode.UInt16:\n            case TypeCode.Int32:\n            case TypeCode.UInt32:\n                ilGenerator.Emit(OpCodes.Ldc_I4_0);\n                break;\n\n            case TypeCode.Int64:\n            case TypeCode.UInt64:\n                ilGenerator.Emit(OpCodes.Ldc_I4_0);\n                ilGenerator.Emit(OpCodes.Conv_I8);\n                break;\n\n            case TypeCode.Single:\n                ilGenerator.Emit(OpCodes.Ldc_R4, default(float));\n                break;\n\n            case TypeCode.Double:\n                ilGenerator.Emit(OpCodes.Ldc_R8, default(double));\n                break;\n\n            case TypeCode.Decimal:\n                ilGenerator.Emit(OpCodes.Ldc_I4_0);\n                ilGenerator.Emit(OpCodes.Newobj, typeof(decimal).GetTypeInfo().GetConstructor([typeof(int)])!);\n                break;\n\n            default:\n                throw new InvalidOperationException(\"Code supposed to be unreachable.\");\n        }\n    }\n}\n\ninternal static class TypeInfoUtils\n{\n    internal static bool AreEquivalent(Type t1, Type t2)\n    {\n        return t1 == t2 || t1.IsEquivalentTo(t2);\n    }\n\n    internal static Type GetNonNullableType(this Type type)\n    {\n        if (type.IsNullableType())\n        {\n            return type.GetGenericArguments()[0];\n        }\n        return type;\n    }\n\n    internal static bool IsLegalExplicitVariantDelegateConversion(Type source, Type dest)\n    {\n        if (!IsDelegate(source) || !IsDelegate(dest) || !source.IsGenericType || !dest.IsGenericType)\n            return false;\n\n        var genericDelegate = source.GetGenericTypeDefinition();\n\n        if (dest.GetGenericTypeDefinition() != genericDelegate)\n            return false;\n\n        var genericParameters = genericDelegate.GetTypeInfo().GetGenericArguments();\n        var sourceArguments = source.GetGenericArguments();\n        var destArguments = dest.GetGenericArguments();\n\n        for (var iParam = 0; iParam < genericParameters.Length; ++iParam)\n        {\n            var sourceArgument = sourceArguments[iParam].GetTypeInfo();\n            var destArgument = destArguments[iParam].GetTypeInfo();\n\n            if (AreEquivalent(sourceArgument, destArgument))\n            {\n                continue;\n            }\n\n            var genericParameter = genericParameters[iParam].GetTypeInfo();\n\n            if (IsInvariant(genericParameter))\n            {\n                return false;\n            }\n\n            if (IsCovariant(genericParameter))\n            {\n                if (!HasReferenceConversion(sourceArgument, destArgument))\n                {\n                    return false;\n                }\n            }\n            else if (IsContravariant(genericParameter))\n            {\n                if (sourceArgument.IsValueType || destArgument.IsValueType)\n                {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    private static bool IsDelegate(Type t)\n    {\n        return t.IsSubclassOf(typeof(MulticastDelegate));\n    }\n\n    private static bool IsInvariant(Type t)\n    {\n        return 0 == (t.GenericParameterAttributes & GenericParameterAttributes.VarianceMask);\n    }\n\n    private static bool IsCovariant(this Type t)\n    {\n        return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Covariant);\n    }\n\n    internal static bool HasReferenceConversion(Type source, Type dest)\n    {\n        // void -> void conversion is handled elsewhere\n        // (it's an identity conversion)\n        // All other void conversions are disallowed.\n        if (source == typeof(void) || dest == typeof(void))\n        {\n            return false;\n        }\n\n        var nnSourceType = source.GetNonNullableType().GetTypeInfo();\n        var nnDestType = dest.GetNonNullableType().GetTypeInfo();\n\n        // Down conversion\n        if (nnSourceType.IsAssignableFrom(nnDestType))\n        {\n            return true;\n        }\n        // Up conversion\n        if (nnDestType.IsAssignableFrom(nnSourceType))\n        {\n            return true;\n        }\n        // Interface conversion\n        if (source.IsInterface || dest.IsInterface)\n        {\n            return true;\n        }\n        // Variant delegate conversion\n        if (IsLegalExplicitVariantDelegateConversion(source, dest))\n            return true;\n\n        // Object conversion\n        if (source == typeof(object) || dest == typeof(object))\n        {\n            return true;\n        }\n        return false;\n    }\n\n    private static bool IsContravariant(Type t)\n    {\n        return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Contravariant);\n    }\n\n    internal static bool IsConvertible(this Type type)\n    {\n        var notNullType = type.Unwrap();\n        if (notNullType.IsEnum)\n        {\n            return true;\n        }\n        switch (Type.GetTypeCode(notNullType))\n        {\n            case TypeCode.Boolean:\n            case TypeCode.Byte:\n            case TypeCode.SByte:\n            case TypeCode.Int16:\n            case TypeCode.Int32:\n            case TypeCode.Int64:\n            case TypeCode.UInt16:\n            case TypeCode.UInt32:\n            case TypeCode.UInt64:\n            case TypeCode.Single:\n            case TypeCode.Double:\n            case TypeCode.Char:\n                return true;\n\n            default:\n                return false;\n        }\n    }\n\n    internal static bool IsUnsigned(Type type)\n    {\n        switch (Type.GetTypeCode(type.Unwrap()))\n        {\n            case TypeCode.Byte:\n            case TypeCode.UInt16:\n            case TypeCode.Char:\n            case TypeCode.UInt32:\n            case TypeCode.UInt64:\n                return true;\n\n            default:\n                return false;\n        }\n    }\n\n    internal static bool IsFloatingPoint(Type type)\n    {\n        return Type.GetTypeCode(type.Unwrap()) switch\n        {\n            TypeCode.Single or TypeCode.Double => true,\n            _ => false,\n        };\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/IOExtension.cs",
    "content": "﻿using System.Text;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\n// ReSharper disable once InconsistentNaming\npublic static class IOExtension\n{\n    /// <summary>\n    /// 把字节数组全部写入当前流\n    /// </summary>\n    /// <param name=\"this\">当前流</param>\n    /// <param name=\"byteArray\">要写入的字节数组</param>\n    /// <returns></returns>\n    public static void Write(this Stream @this, byte[] byteArray)\n    {\n        @this.Write(byteArray, 0, byteArray.Length);\n    }\n\n    /// <summary>\n    /// 把字节数组全部写入当前流\n    /// </summary>\n    /// <param name=\"this\">当前流</param>\n    /// <param name=\"byteArray\">要写入的字节数组</param>\n    /// <returns></returns>\n    public static Task WriteAsync(this Stream @this, byte[] byteArray)\n    {\n        return @this.WriteAsync(byteArray, 0, byteArray.Length);\n    }\n\n    /// <summary>\n    /// 将一个Stream添加到当前Stream中\n    /// </summary>\n    /// <param name=\"this\">当前Stream</param>\n    /// <param name=\"stream\">stream</param>\n    /// <returns></returns>\n    public static Stream Append(this Stream @this, Stream stream)\n    {\n        @this.Write(stream.ToByteArray());\n        return @this;\n    }\n\n    /// <summary>\n    /// 将一个Stream添加到当前Stream中\n    /// </summary>\n    /// <param name=\"this\">当前Stream</param>\n    /// <param name=\"stream\">stream</param>\n    /// <returns></returns>\n    public static async Task<Stream> AppendAsync(this Stream @this, Stream stream)\n    {\n        await @this.WriteAsync(await stream.ToByteArrayAsync());\n        return @this;\n    }\n\n    /// <summary>\n    ///     A Stream extension method that converts the Stream to a byte array.\n    /// </summary>\n    /// <param name=\"this\">The Stream to act on.</param>\n    /// <returns>The Stream as a byte[].</returns>\n    public static byte[] ToByteArray(this Stream @this)\n    {\n        if (@this is MemoryStream ms0)\n            return ms0.ToArray();\n\n        using var ms = new MemoryStream();\n        @this.CopyTo(ms);\n        return ms.ToArray();\n    }\n\n    /// <summary>\n    ///     A Stream extension method that converts the Stream to a byte array.\n    /// </summary>\n    /// <param name=\"this\">The Stream to act on.</param>\n    /// <returns>The Stream as a byte[].</returns>\n    public static async Task<byte[]> ToByteArrayAsync(this Stream @this)\n    {\n        if (@this is MemoryStream ms0)\n            return ms0.ToArray();\n\n        using var ms = new MemoryStream();\n        await @this.CopyToAsync(ms);\n        return ms.ToArray();\n    }\n\n    /// <summary>\n    ///     A Stream extension method that reads a stream to the end.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>\n    ///     The rest of the stream as a string, from the current position to the end. If the current position is at the\n    ///     end of the stream, returns an empty string (\"\").\n    /// </returns>\n    public static string ReadToEnd(this Stream @this)\n    {\n        using var sr = new StreamReader(@this, Encoding.UTF8);\n        return sr.ReadToEnd();\n    }\n\n    /// <summary>\n    ///     A Stream extension method that reads a stream to the end.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"encoding\">The encoding.</param>\n    /// <returns>\n    ///     The rest of the stream as a string, from the current position to the end. If the current position is at the\n    ///     end of the stream, returns an empty string (\"\").\n    /// </returns>\n    public static string ReadToEnd(this Stream @this, Encoding encoding)\n    {\n        using var sr = new StreamReader(@this, encoding);\n        return sr.ReadToEnd();\n    }\n\n    /// <summary>\n    ///     A Stream extension method that reads a stream to the end.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>\n    ///     The rest of the stream as a string, from the current position to the end. If the current position is at the\n    ///     end of the stream, returns an empty string (\"\").\n    /// </returns>\n    public static async Task<string> ReadToEndAsync(this Stream @this)\n    {\n        using var sr = new StreamReader(@this, Encoding.UTF8);\n        return await sr.ReadToEndAsync();\n    }\n\n    /// <summary>\n    ///     A Stream extension method that reads a stream to the end.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <param name=\"encoding\">The encoding.</param>\n    /// <returns>\n    ///     The rest of the stream as a string, from the current position to the end. If the current position is at the\n    ///     end of the stream, returns an empty string (\"\").\n    /// </returns>\n    public static async Task<string> ReadToEndAsync(this Stream @this, Encoding encoding)\n    {\n        using var sr = new StreamReader(@this, encoding);\n        return await sr.ReadToEndAsync();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/JsonSerializeExtension.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class JsonSerializeExtension\n{\n    /// <summary>\n    /// DefaultSerializerSettings\n    /// </summary>\n    internal static readonly JsonSerializerSettings DefaultSerializerSettings = GetDefaultSerializerSettings();\n    internal static readonly JsonSerializerSettings WriteIndentedSerializerSettings =\n        SerializerSettingsWith(s => s.Formatting = Formatting.Indented);\n\n    private static JsonSerializerSettings GetDefaultSerializerSettings() => new()\n    {\n        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\n        MissingMemberHandling = MissingMemberHandling.Ignore,\n        NullValueHandling = NullValueHandling.Ignore,\n    };\n\n    public static JsonSerializerSettings SerializerSettingsWith(Action<JsonSerializerSettings>? action)\n    {\n        if (action is null)\n            return DefaultSerializerSettings;\n\n        var serializerSettings = GetDefaultSerializerSettings();\n        action.Invoke(serializerSettings);\n        return serializerSettings;\n    }\n\n    /// <summary>\n    /// 将object对象转换为Json数据\n    /// </summary>\n    /// <param name=\"obj\">object对象</param>\n    /// <returns>转换后的json字符串</returns>\n    public static string ToJson(this object? obj)\n        => obj.ToJson(false, null);\n\n    /// <summary>\n    /// 将object对象转换为Json数据\n    /// </summary>\n    /// <param name=\"obj\">object对象</param>\n    /// <param name=\"serializerSettings\">序列化设置</param>\n    /// <returns>转换后的json字符串</returns>\n    public static string ToJson(this object obj, JsonSerializerSettings? serializerSettings)\n        => obj.ToJson(false, serializerSettings);\n\n    /// <summary>\n    /// Serialize to Indented Json\n    /// </summary>\n    /// <param name=\"obj\">object</param>\n    /// <returns>To Indented Json</returns>\n    public static string ToIndentedJson(this object obj) => obj.ToJson(false, WriteIndentedSerializerSettings);\n\n    /// <summary>\n    /// 将object对象转换为Json数据\n    /// </summary>\n    /// <param name=\"obj\">目标对象</param>\n    /// <param name=\"isConvertToSingleQuotes\">是否将双引号转成单引号</param>\n    public static string ToJson(this object? obj, bool isConvertToSingleQuotes) => obj.ToJson(isConvertToSingleQuotes, null);\n\n    /// <summary>\n    /// 将object对象转换为Json数据\n    /// </summary>\n    /// <param name=\"obj\">目标对象</param>\n    /// <param name=\"isConvertToSingleQuotes\">是否将双引号转成单引号</param>\n    /// <param name=\"settings\">serializerSettings</param>\n    public static string ToJson(this object? obj, bool isConvertToSingleQuotes, JsonSerializerSettings? settings)\n    {\n        if (obj is null)\n        {\n            return string.Empty;\n        }\n        var result = JsonConvert.SerializeObject(obj, settings ?? DefaultSerializerSettings) ?? string.Empty;\n        if (isConvertToSingleQuotes)\n        {\n            result = result.Replace(\"\\\"\", \"'\");\n        }\n        return result;\n    }\n\n    /// <summary>\n    /// 将Json对象转换为T对象\n    /// </summary>\n    /// <typeparam name=\"T\">对象的类型</typeparam>\n    /// <param name=\"jsonString\">json对象字符串</param>\n    /// <returns>由字符串转换得到的T对象</returns>\n    public static T JsonToObject<T>(this string jsonString)\n        => jsonString.JsonToObject<T>(null);\n\n    /// <summary>\n    /// 将Json对象转换为T对象\n    /// </summary>\n    /// <typeparam name=\"T\">对象的类型</typeparam>\n    /// <param name=\"jsonString\">json对象字符串</param>\n    /// <param name=\"settings\">JsonSerializerSettings</param>\n    /// <returns>由字符串转换得到的T对象</returns>\n    public static T JsonToObject<T>(this string jsonString, JsonSerializerSettings? settings)\n        => Guard.NotNull(JsonConvert.DeserializeObject<T>(jsonString, settings ?? DefaultSerializerSettings));\n\n    /// <summary>\n    /// 对象转换为string，如果是基元类型直接ToString(),如果是Entity则Json序列化\n    /// </summary>\n    /// <param name=\"obj\">要操作的对象</param>\n    /// <returns></returns>\n    public static string ToJsonOrString(this object? obj)\n    {\n        if (obj is null)\n        {\n            return string.Empty;\n        }\n        if (obj is string str)\n        {\n            return str;\n        }\n        if (obj.GetType().IsBasicType())\n        {\n            return Convert.ToString(obj) ?? string.Empty;\n        }\n        return obj.ToJson();\n    }\n\n    /// <summary>\n    /// 字符串数据转换为相应类型的对象，如果是基本数据类型则转换类型，是Entity则Json反序列化\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"jsonString\">字符串</param>\n    /// <returns></returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T StringToType<T>(this string jsonString)\n    {\n        if (typeof(T) == typeof(string))\n        {\n            return (T)(object)jsonString;\n        }\n        if (typeof(T).IsBasicType())\n        {\n            return jsonString.To<T>();\n        }\n        return jsonString.JsonToObject<T>();\n    }\n\n    /// <summary>\n    /// 字符串数据转换为相应类型的对象，如果是基元类型则转换类型，是Entity则Json反序列化\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"jsonString\">json string</param>\n    /// <param name=\"defaultValue\">defaultValue</param>\n    /// <returns></returns>\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T StringToType<T>(this string jsonString, T defaultValue)\n    {\n        if (typeof(T) == typeof(string))\n        {\n            return (T)(object)jsonString;\n        }\n        if (typeof(T).IsBasicType())\n        {\n            return Guard.NotNull(jsonString.ToOrDefault(defaultValue));\n        }\n\n        if (string.IsNullOrWhiteSpace(jsonString))\n        {\n            return defaultValue;\n        }\n        try\n        {\n            return jsonString.JsonToObject<T>();\n        }\n        catch\n        {\n            return defaultValue;\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/NetExtension.cs",
    "content": "﻿using System.Net;\nusing System.Text;\nusing WeihanLi.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class NetExtension\n{\n    #region WebRequest\n\n    /// <summary>\n    ///     A WebRequest extension method that gets the WebRequest response or the WebException response.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The WebRequest response or WebException response.</returns>\n    public static WebResponse GetResponseSafe(this WebRequest @this)\n    {\n        try\n        {\n            return @this.GetResponse();\n        }\n        catch (WebException e)\n        {\n            return Guard.NotNull(e.Response);\n        }\n    }\n\n    /// <summary>\n    ///     A WebRequest extension method that gets the WebRequest response or the WebException response.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The WebRequest response or WebException response.</returns>\n    public static async Task<WebResponse> GetResponseSafeAsync(this WebRequest @this)\n    {\n        try\n        {\n            return await @this.GetResponseAsync().ConfigureAwait(false);\n        }\n        catch (WebException e)\n        {\n            return Guard.NotNull(e.Response);\n        }\n    }\n\n    public static byte[] GetResponseBytes(this WebRequest @this)\n    {\n        using var response = @this.GetResponse();\n        return response.ReadAllBytes();\n    }\n\n    public static async Task<byte[]> GetResponseBytesAsync(this WebRequest @this)\n    {\n        using var response = await @this.GetResponseAsync().ConfigureAwait(false);\n        return await response.ReadAllBytesAsync().ConfigureAwait(false);\n    }\n\n    public static string GetResponseString(this WebRequest @this)\n    {\n        using var response = @this.GetResponse();\n        return response.ReadToEnd();\n    }\n\n    public static async Task<string> GetResponseStringAsync(this WebRequest @this)\n    {\n        using var response = await @this.GetResponseAsync().ConfigureAwait(false);\n        return await response.ReadToEndAsync().ConfigureAwait(false);\n    }\n\n    public static byte[] GetResponseBytesSafe(this WebRequest @this)\n    {\n        using var response = @this.GetResponseSafe();\n        return response.ReadAllBytes();\n    }\n\n    public static async Task<byte[]> GetResponseBytesSafeAsync(this WebRequest @this)\n    {\n        using var response = await @this.GetResponseSafeAsync().ConfigureAwait(false);\n        return await response.ReadAllBytesAsync().ConfigureAwait(false);\n    }\n\n    public static string GetResponseStringSafe(this WebRequest @this)\n    {\n        using var response = @this.GetResponseSafe();\n        return response.ReadToEnd();\n    }\n\n    public static async Task<string> GetResponseStringSafeAsync(this WebRequest @this)\n    {\n        using var response = await @this.GetResponseSafeAsync().ConfigureAwait(false);\n        return await response.ReadToEndAsync().ConfigureAwait(false);\n    }\n\n    #endregion WebRequest\n\n    #region WebResponse\n\n    /// <summary>\n    /// A WebResponse extension method that reads the response stream to byte array.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The response stream as byte array</returns>\n    public static byte[] ReadAllBytes(this WebResponse @this)\n    {\n        using var stream = @this.GetResponseStream();\n        var byteArray = new byte[stream.Length];\n        stream.Write(byteArray, 0, byteArray.Length);\n        return byteArray;\n    }\n\n    /// <summary>\n    /// A WebResponse extension method that reads the response stream to byte array.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The response stream as byte array</returns>\n    public static async Task<byte[]> ReadAllBytesAsync(this WebResponse @this)\n    {\n        using var stream = @this.GetResponseStream();\n        var byteArray = new byte[stream.Length];\n        await stream.WriteAsync(byteArray, 0, byteArray.Length).ConfigureAwait(false);\n        return byteArray;\n    }\n\n    /// <summary>\n    ///     A WebResponse extension method that reads the response stream to the end.\n    /// </summary>\n    /// <param name=\"response\">The response to act on.</param>\n    /// <returns>The response stream as a string, from the current position to the end.</returns>\n    public static string ReadToEnd(this WebResponse response)\n    {\n        using var stream = response.GetResponseStream();\n        return stream.ReadToEnd(Encoding.UTF8);\n    }\n\n    /// <summary>\n    ///     A WebResponse extension method that reads the response stream to the end.\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The response stream as a string, from the current position to the end.</returns>\n    public static async Task<string> ReadToEndAsync(this WebResponse @this)\n    {\n        using var stream = @this.GetResponseStream();\n        return await stream.ReadToEndAsync().ConfigureAwait(false);\n    }\n\n    #endregion WebResponse\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/ProcessExtension.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Text;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class ProcessExtension\n{\n    public static ProcessStartInfo WithEnv(this ProcessStartInfo processStartInfo, string name, string value)\n    {\n        Guard.NotNull(processStartInfo);\n        processStartInfo.Environment[name] = value;\n        return processStartInfo;\n    }\n\n#if NETSTANDARD\n    public static Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)\n    {\n        Guard.NotNull(process);\n        process.EnableRaisingEvents = true;\n        var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);\n        try\n        {\n            process.Exited += EventHandler;\n            tcs.Task.Wait(cancellationToken);\n        }\n        catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)\n        {\n            process.TryKill();\n            tcs.TrySetCanceled();\n        }\n        catch (Exception ex)\n        {\n            tcs.SetException(ex);\n        }\n        finally\n        {\n            process.Exited -= EventHandler;\n        }\n\n        return tcs.Task;\n\n        void EventHandler(object o, EventArgs eventArgs) => tcs.TrySetResult();\n    }\n#endif\n\n    /// <summary>\n    /// Execute command with a process\n    /// </summary>\n    /// <param name=\"processStartInfo\">processStartInfo</param>\n    /// <returns>process exit code</returns>\n    public static int Execute(this ProcessStartInfo processStartInfo)\n    {\n        using var process = new Process();\n        process.StartInfo = processStartInfo;\n        process.Start();\n        process.WaitForExit();\n        return process.ExitCode;\n    }\n\n    /// <summary>\n    /// Execute command with a process\n    /// </summary>\n    /// <param name=\"processStartInfo\">processStartInfo</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>process exit code</returns>\n    public static async Task<int> ExecuteAsync(this ProcessStartInfo processStartInfo, CancellationToken cancellationToken = default)\n    {\n        using var process = new Process();\n        process.StartInfo = processStartInfo;\n        process.Start();\n        using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ApplicationHelper.ExitToken);\n        cts.Token.Register((p) => ((Process)p!).TryKill(), process);\n        await process.WaitForExitAsync(cts.Token);\n        return process.ExitCode;\n    }\n\n    /// <summary>\n    /// Get process execute result\n    /// </summary>\n    /// <param name=\"psi\">ProcessStartInfo</param>\n    /// <returns>process output and exitCode</returns>\n    public static CommandResult GetResult(this ProcessStartInfo psi)\n    {\n        var stdOutStringBuilder = new StringBuilder();\n        using var stdOut = new StringWriter(stdOutStringBuilder);\n        var stdErrStringBuilder = new StringBuilder();\n        using var stdErr = new StringWriter(stdErrStringBuilder);\n        var exitCode = -1;\n        int? processId = null;\n        Action<Process> processStartAction = p => processId = p.Id;\n\n        try\n        {\n            using var process = psi.ExecuteProcess(stdOut, stdErr, processStartAction);\n            exitCode = process.ExitCode;\n        }\n        catch (Win32Exception win32Exception)\n        {\n            exitCode = win32Exception.ErrorCode;\n        }\n        return new(exitCode, stdOutStringBuilder.ToString(), stdErrStringBuilder.ToString())\n        {\n            ProcessId = processId\n        };\n    }\n\n    /// <summary>\n    /// Get process execute result\n    /// </summary>\n    /// <param name=\"psi\">ProcessStartInfo</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>process output and exitCode</returns>\n    public static async Task<CommandResult> GetResultAsync(this ProcessStartInfo psi, CancellationToken cancellationToken = default)\n    {\n        var stdOutStringBuilder = new StringBuilder();\n#if NET\n        await\n#endif\n        using var stdOut = new StringWriter(stdOutStringBuilder);\n        var stdErrStringBuilder = new StringBuilder();\n#if NET\n        await\n#endif\n        using var stdErr = new StringWriter(stdErrStringBuilder);\n        var exitCode = -1;\n        int? processId = null;\n        Action<Process> processStartAction = p => processId = p.Id;\n        try\n        {\n            using var process = await psi.ExecuteProcessAsync(stdOut, stdErr, processStartAction.WrapTask(), cancellationToken);\n            exitCode = process.ExitCode;\n        }\n        catch (Win32Exception win32Exception)\n        {\n            exitCode = win32Exception.ErrorCode;\n        }\n        return new(exitCode, stdOutStringBuilder.ToString(), stdErrStringBuilder.ToString())\n        {\n            ProcessId = processId\n        };\n    }\n\n    /// <summary>\n    /// Wait for process exit and get exit code\n    /// </summary>\n    /// <param name=\"psi\">Process is started from this information</param>\n    /// <param name=\"stdOut\">Defaults to Console.Out</param>\n    /// <param name=\"stdErr\">Defaults to Console.Error</param>\n    /// <returns>Process exit code</returns>\n    public static int GetExitCode(this ProcessStartInfo psi, TextWriter? stdOut = null,\n        TextWriter? stdErr = null)\n    {\n        try\n        {\n            using var process = psi.ExecuteProcess(stdOut, stdErr);\n            return process.ExitCode;\n        }\n        catch (Win32Exception win32Exception)\n        {\n            return win32Exception.ErrorCode;\n        }\n    }\n\n    /// <summary>\n    /// Wait for process exit and get exit code\n    /// </summary>\n    /// <param name=\"psi\">Process is started from this information</param>\n    /// <param name=\"stdOut\">Defaults to Console.Out</param>\n    /// <param name=\"stdErr\">Defaults to Console.Error</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>Process exit code</returns>\n    public static async Task<int> GetExitCodeAsync(this ProcessStartInfo psi, TextWriter? stdOut = null,\n        TextWriter? stdErr = null, CancellationToken cancellationToken = default)\n    {\n        try\n        {\n            using var process = await psi.ExecuteProcessAsync(stdOut, stdErr, null, cancellationToken);\n            return process.ExitCode;\n        }\n        catch (Win32Exception win32Exception)\n        {\n            return win32Exception.ErrorCode;\n        }\n    }\n\n    /// <summary>\n    /// Execute process\n    /// </summary>\n    /// <param name=\"psi\">Process is started from this information</param>\n    /// <param name=\"stdOut\">Defaults to Console.Out</param>\n    /// <param name=\"stdErr\">Defaults to Console.Error</param>\n    /// <param name=\"processStartAction\">Action to execute when process start</param>\n    /// <returns>Process executed</returns>\n    public static Process ExecuteProcess(this ProcessStartInfo psi, TextWriter? stdOut = null,\n        TextWriter? stdErr = null, Action<Process>? processStartAction = null)\n    {\n        psi.RedirectStandardOutput = stdOut != null;\n        psi.RedirectStandardError = stdErr != null;\n\n        var process = new Process();\n        process.StartInfo = psi;\n        process.OutputDataReceived += (_, e) =>\n        {\n            if (e.Data != null)\n                stdOut?.WriteLine(e.Data);\n        };\n        process.ErrorDataReceived += (_, e) =>\n        {\n            if (e.Data != null)\n                stdErr?.WriteLine(e.Data);\n        };\n\n        process.Start();\n        processStartAction?.Invoke(process);\n\n        process.BeginOutputReadLine();\n        process.BeginErrorReadLine();\n        process.WaitForExit();\n\n        return process;\n    }\n\n    /// <summary>\n    /// Execute process\n    /// </summary>\n    /// <param name=\"psi\">Process is started from this information</param>\n    /// <param name=\"stdOut\">Defaults to Console.Out</param>\n    /// <param name=\"stdErr\">Defaults to Console.Error</param>\n    /// <param name=\"processStartAction\">Action to execute when process start</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>Process exit code</returns>\n    public static async Task<Process> ExecuteProcessAsync(this ProcessStartInfo psi, TextWriter? stdOut = null,\n        TextWriter? stdErr = null, Func<Process, Task>? processStartAction = null, CancellationToken cancellationToken = default)\n    {\n        psi.RedirectStandardOutput = stdOut != null;\n        psi.RedirectStandardError = stdErr != null;\n\n        var process = new Process();\n        process.StartInfo = psi;\n        var stdOutComplete = new TaskCompletionSource();\n        var stdErrComplete = new TaskCompletionSource();\n        process.OutputDataReceived += (_, e) =>\n        {\n            if (e.Data != null)\n                stdOut?.WriteLine(e.Data);\n            else\n                stdOutComplete.SetResult();\n        };\n        process.ErrorDataReceived += (_, e) =>\n        {\n            if (e.Data != null)\n                stdErr?.WriteLine(e.Data);\n            else\n                stdErrComplete.SetResult();\n        };\n\n        process.Start();\n        if (processStartAction is not null)\n        {\n            await processStartAction.Invoke(process);\n        }\n\n        process.BeginOutputReadLine();\n        process.BeginErrorReadLine();\n        using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ApplicationHelper.ExitToken);\n        cts.Token.Register((p) => ((Process)p!).TryKill(), process);\n        await Task.WhenAll(process.WaitForExitAsync(cts.Token), stdOutComplete.Task, stdErrComplete.Task);\n        return process;\n    }\n\n    /// <summary>\n    /// Try kill process\n    /// </summary>\n    /// <param name=\"process\"></param>\n    /// <param name=\"entireProcessTree\"></param>\n    /// <returns></returns>\n    public static bool TryKill(this Process process, bool entireProcessTree = true)\n    {\n        return\n#if NET\n        process.Try(x => x.Kill(entireProcessTree))\n#else\n        process.Try(x => x.Kill())\n#endif\n            ;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/PropertiesExtensions.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Abstractions;\n\nnamespace WeihanLi.Common.Extensions;\n\npublic static class PropertiesExtensions\n{\n    public static T? GetProperty<T>(this IDictionary<string, object?> properties, string key)\n    {\n        return Guard.NotNull(properties).TryGetValue(key, out var value) ? (T?)value : default;\n    }\n\n    public static void SetProperty<T>(this IDictionary<string, object?> properties, string key, T value)\n    {\n        Guard.NotNull(properties)[key] = value;\n    }\n\n    public static T? GetProperty<T>(this IProperties propertiesHolder, string key)\n    {\n        Guard.NotNull(propertiesHolder);\n        return propertiesHolder.Properties.TryGetValue(key, out var value) ? (T?)value : default;\n    }\n\n    public static void SetProperty<T>(this IProperties propertiesHolder, string key, T value)\n    {\n        Guard.NotNull(propertiesHolder);\n        propertiesHolder.Properties[key] = value;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/QueryableExtension.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Models;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class QueryableExtension\n{\n    public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, bool condition)\n    {\n        return condition ? Guard.NotNull(source, nameof(source)).Where(predicate) : source;\n    }\n\n    public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, Func<bool> conditionFunc)\n    {\n        return conditionFunc() ? Guard.NotNull(source, nameof(source)).Where(predicate) : source;\n    }\n\n    /// <summary>\n    /// Converts the specified source to <see cref=\"IPagedListResult{T}\"/> by the specified <paramref name=\"pageNumber\"/> and <paramref name=\"pageSize\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of the source.</typeparam>\n    /// <param name=\"source\">The source to paging.</param>\n    /// <param name=\"pageNumber\">The number of the page, start from 1.</param>\n    /// <param name=\"pageSize\">The size of the page.</param>\n    /// <returns>An instance of  implements <see cref=\"IPagedListResult{T}\"/> interface.</returns>\n    public static ListResultWithTotal<T> ToListResultWithTotal<T>(this IQueryable<T> source, int pageNumber, int pageSize)\n    {\n        if (pageNumber <= 0)\n        {\n            pageNumber = 1;\n        }\n        if (pageSize <= 0)\n        {\n            pageSize = 10;\n        }\n        var count = source.Count();\n        if (count == 0)\n        {\n            return ListResultWithTotal<T>.Empty;\n        }\n\n        if (pageNumber > 1)\n        {\n            source = source.Skip((pageNumber - 1) * pageSize);\n        }\n        var items = source.Take(pageSize).ToArray();\n\n        return new ListResultWithTotal<T>()\n        {\n            TotalCount = count,\n            Data = items\n        };\n    }\n\n    /// <summary>\n    /// Converts the specified source to <see cref=\"IPagedListResult{T}\"/> by the specified <paramref name=\"pageNumber\"/> and <paramref name=\"pageSize\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of the source.</typeparam>\n    /// <param name=\"source\">The source to paging.</param>\n    /// <param name=\"pageNumber\">The number of the page, start from 1.</param>\n    /// <param name=\"pageSize\">The size of the page.</param>\n    /// <returns>An instance of  implements <see cref=\"IPagedListResult{T}\"/> interface.</returns>\n    public static PagedListResult<T> ToPagedList<T>(this IQueryable<T> source, int pageNumber, int pageSize)\n    {\n        if (pageNumber <= 0)\n        {\n            pageNumber = 1;\n        }\n        if (pageSize <= 0)\n        {\n            pageSize = 10;\n        }\n        var count = source.Count();\n        if (count == 0)\n        {\n            return PagedListResult<T>.Empty;\n        }\n\n        if (pageNumber > 1)\n        {\n            source = source.Skip((pageNumber - 1) * pageSize);\n        }\n        var items = source.Take(pageSize).ToArray();\n\n        return new PagedListResult<T>()\n        {\n            PageNumber = pageNumber,\n            PageSize = pageSize,\n            TotalCount = count,\n            Data = items\n        };\n    }\n\n    /// <summary>\n    /// OrderBy propertyName\n    /// </summary>\n    /// <typeparam name=\"T\">T</typeparam>\n    /// <param name=\"source\">source</param>\n    /// <param name=\"propertyName\">propertyName to orderBy</param>\n    /// <param name=\"isAsc\">is ascending</param>\n    /// <returns></returns>\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool isAsc = false)\n    {\n        Guard.NotNull(source);\n        Guard.NotNullOrWhiteSpace(propertyName);\n\n        var type = typeof(T);\n        var arg = Expression.Parameter(type, \"x\");\n        var propertyInfo = type.GetProperty(propertyName);\n        if (null == propertyInfo)\n        {\n            throw new InvalidOperationException($\"{propertyName} does not exists\");\n        }\n\n        Expression expression = Expression.Property(arg, propertyInfo);\n        type = propertyInfo.PropertyType;\n\n        var delegateType = typeof(Func<,>).MakeGenericType(type, type);\n        var lambda = Expression.Lambda(delegateType, expression, arg);\n\n        var methodName = isAsc ? \"OrderBy\" : \"OrderByDescending\";\n        var result = typeof(Queryable).GetMethods().Single(\n                method => method.Name == methodName\n                          && method.IsGenericMethodDefinition\n                          && method.GetGenericArguments().Length == 2\n                          && method.GetParameters().Length == 2)\n            .MakeGenericMethod(type, type)\n            .Invoke(null, new object[] { source, lambda });\n        return (IQueryable<T>)Guard.NotNull(result);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/ReflectionExtension.cs",
    "content": "﻿using System.ComponentModel;\nusing System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing WeihanLi.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class ReflectionExtension\n{\n    public static MethodInfo? GetMethodBySignature([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] this Type type, MethodInfo method)\n    {\n        Guard.NotNull(type);\n        Guard.NotNull(method);\n\n        var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)\n            .Where(x => x.Name.Equals(method.Name))\n            .ToArray();\n\n        var parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray();\n        if (method.ContainsGenericParameters)\n        {\n            foreach (var info in methods)\n            {\n                var innerParams = info.GetParameters();\n                if (innerParams.Length != parameterTypes.Length)\n                {\n                    continue;\n                }\n\n                var idx = 0;\n                foreach (var param in innerParams)\n                {\n                    if (!param.ParameterType.IsGenericParameter\n                        && !parameterTypes[idx].IsGenericParameter\n                        && param.ParameterType != parameterTypes[idx]\n                    )\n                    {\n                        break;\n                    }\n\n                    idx++;\n                }\n                if (idx < parameterTypes.Length)\n                {\n                    continue;\n                }\n\n                return info;\n            }\n\n            return null;\n        }\n\n        var baseMethod = type.GetMethod(method.Name, parameterTypes);\n        return baseMethod;\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used.\")]\n    public static MethodInfo? GetBaseMethod(this MethodInfo? currentMethod)\n    {\n        if (null == currentMethod?.DeclaringType?.BaseType)\n            return null;\n\n        return currentMethod.DeclaringType.BaseType.GetMethodBySignature(currentMethod);\n    }\n\n    public static bool IsVisibleAndVirtual(this PropertyInfo property)\n    {\n        Guard.NotNull(property);\n        return (property.CanRead && property.GetMethod!.IsVisibleAndVirtual()) ||\n               (property.CanWrite && property.SetMethod!.IsVisibleAndVirtual());\n    }\n\n    public static bool IsVisibleAndVirtual(this MethodInfo method)\n    {\n        Guard.NotNull(method);\n        if (method.IsStatic || method.IsFinal)\n        {\n            return false;\n        }\n        return method.IsVirtual &&\n               (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly);\n    }\n\n    public static bool IsVisible(this MethodBase method)\n    {\n        Guard.NotNull(method);\n        return method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly;\n    }\n\n    /// <summary>\n    /// An object extension method that gets DisplayName if DisplayAttribute does not exist,return the MemberName\n    /// </summary>\n    /// <param name=\"this\">The @this to act on.</param>\n    /// <returns>The custom attribute.</returns>\n    public static string GetDisplayName(this MemberInfo @this)\n        => Guard.NotNull(@this).GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? @this.GetCustomAttribute<DisplayAttribute>()?.Name ?? @this.Name;\n\n    /// <summary>\n    /// GetColumnName\n    /// </summary>\n    /// <returns></returns>\n    public static string GetColumnName(this PropertyInfo propertyInfo) => Guard.NotNull(propertyInfo).GetCustomAttribute<ColumnAttribute>()?.Name ?? propertyInfo.Name;\n\n    /// <summary>\n    /// GetDescription\n    /// </summary>\n    /// <returns></returns>\n    public static string GetDescription(this MemberInfo @this) => Guard.NotNull(@this).GetCustomAttribute<DescriptionAttribute>()?.Description ?? @this.Name;\n\n    public static Func<T, object?>? GetValueGetter<T>(this PropertyInfo propertyInfo)\n    {\n        Guard.NotNull(propertyInfo);\n        return StrongTypedCache<T>.PropertyValueGetters.GetOrAdd(propertyInfo, prop =>\n        {\n            if (!prop.CanRead)\n                return null;\n\n            var instance = Expression.Parameter(typeof(T), \"i\");\n            var property = Expression.Property(instance, prop);\n            var convert = Expression.TypeAs(property, typeof(object));\n            return (Func<T, object>)Expression.Lambda(convert, instance).Compile();\n        });\n    }\n\n    public static Func<object, object?>? GetValueGetter(this PropertyInfo propertyInfo)\n    {\n        Guard.NotNull(propertyInfo);\n        return CacheUtil.PropertyValueGetters.GetOrAdd(propertyInfo, prop =>\n        {\n            if (!prop.CanRead)\n                return null;\n\n            Debug.Assert(propertyInfo.DeclaringType != null);\n\n            var instance = Expression.Parameter(typeof(object), \"obj\");\n            var getterCall = Expression.Call(propertyInfo.DeclaringType!.IsValueType\n                ? Expression.Unbox(instance, propertyInfo.DeclaringType)\n                : Expression.Convert(instance, propertyInfo.DeclaringType), prop.GetGetMethod()!);\n            var castToObject = Expression.Convert(getterCall, typeof(object));\n            return (Func<object, object>)Expression.Lambda(castToObject, instance).Compile();\n        });\n    }\n\n    public static Action<T, object?>? GetValueSetter<T>(this PropertyInfo propertyInfo) where T : class\n    {\n        Guard.NotNull(propertyInfo);\n        return StrongTypedCache<T>.PropertyValueSetters.GetOrAdd(propertyInfo, prop =>\n        {\n            if (!prop.CanWrite)\n                return null;\n\n            var instance = Expression.Parameter(typeof(T), \"i\");\n            var argument = Expression.Parameter(typeof(object), \"a\");\n            var setterCall = Expression.Call(instance, prop.GetSetMethod()!, Expression.Convert(argument, prop.PropertyType));\n            return (Action<T, object?>)Expression.Lambda(setterCall, instance, argument).Compile();\n        });\n    }\n\n    public static Action<object, object?>? GetValueSetter(this PropertyInfo propertyInfo)\n    {\n        Guard.NotNull(propertyInfo, nameof(propertyInfo));\n        return CacheUtil.PropertyValueSetters.GetOrAdd(propertyInfo, prop =>\n        {\n            if (!prop.CanWrite)\n                return null;\n\n            var obj = Expression.Parameter(typeof(object), \"o\");\n            var value = Expression.Parameter(typeof(object));\n\n            // Note that we are using Expression.Unbox for value types and Expression.Convert for reference types\n            var expr =\n            Expression.Lambda<Action<object, object?>>(\n                Expression.Call(\n                    propertyInfo.DeclaringType!.IsValueType\n                        ? Expression.Unbox(obj, propertyInfo.DeclaringType)\n                        : Expression.Convert(obj, propertyInfo.DeclaringType),\n                    propertyInfo.GetSetMethod()!,\n                    Expression.Convert(value, propertyInfo.PropertyType)),\n                obj, value);\n            return expr.Compile();\n        });\n    }\n\n    /// <summary>\n    ///     Retrieves a custom attribute applied to a specified assembly. Parameters specify the assembly and the type of\n    ///     the custom attribute to search for.\n    /// </summary>\n    /// <param name=\"element\">An object derived from the  class that describes a reusable collection of modules.</param>\n    /// <param name=\"attributeType\">The type, or a base type, of the custom attribute to search for.</param>\n    /// <returns>\n    ///     A reference to the single custom attribute of type  that is applied to , or null if there is no such\n    ///     attribute.\n    /// </returns>\n    public static Attribute? GetCustomAttribute(this Assembly element, Type attributeType)\n    {\n        Guard.NotNull(element);\n        return Attribute.GetCustomAttribute(element, attributeType);\n    }\n\n    /// <summary>\n    ///     Retrieves a custom attribute applied to an assembly. Parameters specify the assembly, the type of the custom\n    ///     attribute to search for, and an ignored search option.\n    /// </summary>\n    /// <param name=\"element\">An object derived from the  class that describes a reusable collection of modules.</param>\n    /// <param name=\"attributeType\">The type, or a base type, of the custom attribute to search for.</param>\n    /// <param name=\"inherit\">This parameter is ignored, and does not affect the operation of this method.</param>\n    /// <returns>\n    ///     A reference to the single custom attribute of type  that is applied to , or null if there is no such\n    ///     attribute.\n    /// </returns>\n    public static Attribute? GetCustomAttribute(this Assembly element, Type attributeType, bool inherit)\n    {\n        Guard.NotNull(element);\n        return Attribute.GetCustomAttribute(element, attributeType, inherit);\n    }\n\n    /// <summary>\n    ///     Retrieves an array of the custom attributes applied to an assembly. Parameters specify the assembly, and the\n    ///     type of the custom attribute to search for.\n    /// </summary>\n    /// <param name=\"element\">An object derived from the  class that describes a reusable collection of modules.</param>\n    /// <param name=\"attributeType\">The type, or a base type, of the custom attribute to search for.</param>\n    /// <returns>\n    ///     An  array that contains the custom attributes of type  applied to , or an empty array if no such custom\n    ///     attributes exist.\n    /// </returns>\n    public static Attribute[] GetCustomAttributes(this Assembly element, Type attributeType)\n    {\n        Guard.NotNull(element);\n        return Attribute.GetCustomAttributes(element, attributeType);\n    }\n\n    /// <summary>\n    ///     Retrieves an array of the custom attributes applied to an assembly. Parameters specify the assembly, the type\n    ///     of the custom attribute to search for, and an ignored search option.\n    /// </summary>\n    /// <param name=\"element\">An object derived from the  class that describes a reusable collection of modules.</param>\n    /// <param name=\"attributeType\">The type, or a base type, of the custom attribute to search for.</param>\n    /// <param name=\"inherit\">This parameter is ignored, and does not affect the operation of this method.</param>\n    /// <returns>\n    ///     An  array that contains the custom attributes of type  applied to , or an empty array if no such custom\n    ///     attributes exist.\n    /// </returns>\n    public static Attribute[] GetCustomAttributes(this Assembly element, Type attributeType, bool inherit)\n    {\n        Guard.NotNull(element);\n        return Attribute.GetCustomAttributes(element, attributeType, inherit);\n    }\n\n    /// <summary>\n    ///     Retrieves an array of the custom attributes applied to an assembly. A parameter specifies the assembly.\n    /// </summary>\n    /// <param name=\"element\">An object derived from the  class that describes a reusable collection of modules.</param>\n    /// <returns>\n    ///     An  array that contains the custom attributes applied to , or an empty array if no such custom attributes\n    ///     exist.\n    /// </returns>\n    public static Attribute[] GetCustomAttributes(this Assembly element)\n    {\n        Guard.NotNull(element);\n        return Attribute.GetCustomAttributes(element);\n    }\n\n    /// <summary>\n    ///     Retrieves an array of the custom attributes applied to an assembly. Parameters specify the assembly, and an\n    ///     ignored search option.\n    /// </summary>\n    /// <param name=\"element\">An object derived from the  class that describes a reusable collection of modules.</param>\n    /// <param name=\"inherit\">This parameter is ignored, and does not affect the operation of this method.</param>\n    /// <returns>\n    ///     An  array that contains the custom attributes applied to , or an empty array if no such custom attributes\n    ///     exist.\n    /// </returns>\n    public static Attribute[] GetCustomAttributes(this Assembly element, bool inherit)\n    {\n        Guard.NotNull(element);\n        return Attribute.GetCustomAttributes(element, inherit);\n    }\n\n    /// <summary>\n    ///     Determines whether any custom attributes are applied to an assembly. Parameters specify the assembly, and the\n    ///     type of the custom attribute to search for.\n    /// </summary>\n    /// <param name=\"element\">An object derived from the  class that describes a reusable collection of modules.</param>\n    /// <param name=\"attributeType\">The type, or a base type, of the custom attribute to search for.</param>\n    /// <returns>true if a custom attribute of type  is applied to ; otherwise, false.</returns>\n    public static bool IsDefined(this Assembly element, Type attributeType)\n    {\n        Guard.NotNull(element);\n        return Attribute.IsDefined(element, attributeType);\n    }\n\n    /// <summary>\n    ///     Determines whether any custom attributes are applied to an assembly. Parameters specify the assembly, the\n    ///     type of the custom attribute to search for, and an ignored search option.\n    /// </summary>\n    /// <param name=\"element\">An object derived from the  class that describes a reusable collection of modules.</param>\n    /// <param name=\"attributeType\">The type, or a base type, of the custom attribute to search for.</param>\n    /// <param name=\"inherit\">This parameter is ignored, and does not affect the operation of this method.</param>\n    /// <returns>true if a custom attribute of type  is applied to ; otherwise, false.</returns>\n    public static bool IsDefined(this Assembly element, Type attributeType, bool inherit)\n    {\n        Guard.NotNull(element);\n        return Attribute.IsDefined(element, attributeType, inherit);\n    }\n\n    /// <summary>\n    /// Get Assembly Display Version\n    /// </summary>\n    /// <param name=\"assembly\">assembly</param>\n    /// <returns>Version info</returns>\n    public static string? GetDisplayVersion(this Assembly assembly)\n    {\n        // The package version is stamped into the assembly's AssemblyInformationalVersionAttribute at build time, followed by a '+' and\n        // the commit hash, e.g.:\n        // [assembly: AssemblyInformationalVersion(\"8.0.0-preview.2.23604.7+e7762a46d31842884a0bc72c92e07ba700c99bf5\")]\n\n        var version = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;\n\n        if (version is not null)\n        {\n            var plusIndex = version.IndexOf('+');\n\n            if (plusIndex > 0)\n            {\n                return version[..plusIndex];\n            }\n\n            return version;\n        }\n\n        // Fallback to the file version, which is based on the CI build number, and then fallback to the assembly version, which is\n        // product stable version, e.g. 8.0.0.0\n        version = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version\n                  ?? assembly.GetCustomAttribute<AssemblyVersionAttribute>()?.Version;\n\n        return version;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/ServiceCollectionExtension.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection.Extensions;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\n\n// ReSharper disable once CheckNamespace\nnamespace Microsoft.Extensions.DependencyInjection;\n\npublic interface IServiceModule\n{\n    void ConfigureServices(IServiceCollection services);\n}\n\npublic static class ServiceCollectionExtension\n{\n    /// <summary>\n    /// RegisterAssemblyTypes\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyTypes(this IServiceCollection services, params Assembly[] assemblies)\n        => RegisterAssemblyTypes(services, null, ServiceLifetime.Singleton, assemblies);\n\n    /// <summary>\n    /// RegisterAssemblyTypes\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyTypes(this IServiceCollection services,\n        ServiceLifetime serviceLifetime, params Assembly[] assemblies)\n        => RegisterAssemblyTypes(services, null, serviceLifetime, assemblies);\n\n    /// <summary>\n    /// RegisterAssemblyTypes\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyTypes(this IServiceCollection services,\n        Func<Type, bool>? typesFilter, params Assembly[] assemblies)\n        => RegisterAssemblyTypes(services, typesFilter, ServiceLifetime.Singleton, assemblies);\n\n    /// <summary>\n    /// RegisterAssemblyTypes\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyTypes(this IServiceCollection services, Func<Type, bool>? typesFilter, ServiceLifetime serviceLifetime, params Assembly[] assemblies)\n    {\n        if (Guard.NotNull(assemblies, nameof(assemblies)).Length == 0)\n        {\n            assemblies = ReflectHelper.GetAssemblies();\n        }\n\n        var types = assemblies\n            .Select(assembly => assembly.GetTypes())\n            .SelectMany(t => t)\n            .Where(t => !t.IsAbstract)\n            ;\n        if (typesFilter != null)\n        {\n            types = types.Where(typesFilter);\n        }\n\n        foreach (var type in types)\n        {\n            services.Add(new ServiceDescriptor(type, type, serviceLifetime));\n        }\n\n        return services;\n    }\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyTypesAsImplementedInterfaces(this IServiceCollection services,\n        params Assembly[] assemblies)\n        => RegisterAssemblyTypesAsImplementedInterfaces(services, typesFilter: null, ServiceLifetime.Singleton, assemblies);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyTypesAsImplementedInterfaces(this IServiceCollection services,\n        ServiceLifetime serviceLifetime, params Assembly[] assemblies)\n        => RegisterAssemblyTypesAsImplementedInterfaces(services, typesFilter: null, serviceLifetime, assemblies);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces, singleton by default\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyTypesAsImplementedInterfaces(this IServiceCollection services, Func<Type, bool> typesFilter, params Assembly[] assemblies)\n        => RegisterAssemblyTypesAsImplementedInterfaces(services, typesFilter: typesFilter, ServiceLifetime.Singleton, assemblies);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyTypesAsImplementedInterfaces(\n        this IServiceCollection services, Func<Type, bool>? typesFilter, ServiceLifetime serviceLifetime,\n        params Assembly[] assemblies)\n        => RegisterAssemblyTypesAsImplementedInterfaces(services, typesFilter, null, serviceLifetime, assemblies);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"typesFilter\">filter types to register</param>\n    /// <param name=\"interfaceTypeFilter\">filter interface types to register</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyTypesAsImplementedInterfaces(this IServiceCollection services, Func<Type, bool>? typesFilter, Func<Type, bool>? interfaceTypeFilter, ServiceLifetime serviceLifetime, params Assembly[] assemblies)\n    {\n        if (Guard.NotNull(assemblies, nameof(assemblies)).Length == 0)\n        {\n            assemblies = ReflectHelper.GetAssemblies();\n        }\n\n        var types = assemblies\n            .Select(assembly => assembly.GetTypes())\n            .SelectMany(t => t)\n            .Where(t => !t.IsAbstract)\n            ;\n        if (typesFilter != null)\n        {\n            types = types.Where(typesFilter);\n        }\n\n        foreach (var type in types)\n        {\n            foreach (var implementedInterface in type.GetImplementedInterfaces())\n            {\n                if (interfaceTypeFilter?.Invoke(implementedInterface) != false)\n                {\n                    services.Add(new ServiceDescriptor(implementedInterface, type, serviceLifetime));\n                }\n            }\n        }\n\n        return services;\n    }\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"type\">type</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <returns>services</returns>\n    public static IServiceCollection RegisterTypeAsImplementedInterfaces(this IServiceCollection services,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,\n        ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)\n        => RegisterTypeAsImplementedInterfaces(services, type, null, serviceLifetime);\n\n    /// <summary>\n    /// RegisterTypeAsImplementedInterfaces\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"type\">type</param>\n    /// <param name=\"interfaceTypeFilter\">interfaceTypeFilter</param>\n    /// <param name=\"serviceLifetime\">service lifetime</param>\n    /// <returns>services</returns>\n    public static IServiceCollection RegisterTypeAsImplementedInterfaces(this IServiceCollection services,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,\n        Func<Type, bool>? interfaceTypeFilter, ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)\n    {\n        Guard.NotNull(type);\n        foreach (var interfaceType in type.GetImplementedInterfaces())\n        {\n            if (interfaceTypeFilter?.Invoke(interfaceType) != false)\n            {\n                services.Add(new ServiceDescriptor(interfaceType, type, serviceLifetime));\n            }\n        }\n\n        return services;\n    }\n\n    /// <summary>\n    /// Register Module\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"module\">service module</param>\n    /// <returns>services</returns>\n    public static IServiceCollection RegisterModule<TServiceModule>(this IServiceCollection services, TServiceModule module)\n        where TServiceModule : IServiceModule\n    {\n        Guard.NotNull(module, nameof(module)).ConfigureServices(services);\n        return services;\n    }\n\n    /// <summary>\n    /// RegisterAssemblyModules\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"assemblies\">assemblies</param>\n    /// <returns>services</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IServiceCollection RegisterAssemblyModules(\n        this IServiceCollection services, params Assembly[] assemblies)\n    {\n        Guard.NotNull(assemblies, nameof(assemblies));\n        if (assemblies.Length == 0)\n        {\n            assemblies = ReflectHelper.GetAssemblies();\n        }\n        foreach (var type in assemblies.SelectMany(ass => ass.GetTypes())\n            .Where(t => t is { IsClass: true, IsAbstract: false } && typeof(IServiceModule).IsAssignableFrom(t))\n        )\n        {\n            try\n            {\n                if (Activator.CreateInstance(type) is IServiceModule module)\n                {\n                    module.ConfigureServices(services);\n                }\n            }\n            catch (Exception e)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(e);\n            }\n        }\n\n        return services;\n    }\n\n    /// <summary>\n    /// Register decorator for TService\n    /// </summary>\n    /// <typeparam name=\"TService\">service type</typeparam>\n    /// <typeparam name=\"TDecorator\">decorator type</typeparam>\n    /// <param name=\"services\">services</param>\n    /// <returns>services</returns>\n    public static IServiceCollection Decorate<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TDecorator>(this IServiceCollection services)\n         where TService : class\n         where TDecorator : class, TService\n    {\n        return services.Decorate(typeof(TService), typeof(TDecorator));\n    }\n\n    /// <summary>\n    /// Register service decorator\n    /// </summary>\n    /// <param name=\"services\">services</param>\n    /// <param name=\"serviceType\">serviceType</param>\n    /// <param name=\"decoratorType\">decoratorType</param>\n    /// <returns>services</returns>\n    /// <exception cref=\"InvalidOperationException\">throw exception when serviceType not registered</exception>\n    public static IServiceCollection Decorate(this IServiceCollection services, Type serviceType,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type decoratorType)\n    {\n        var service = services.LastOrDefault(x => x.ServiceType == serviceType) ?? throw new InvalidOperationException(\"The service is not registered, service need to be registered before decorating\");\n        var objectFactory = ActivatorUtilities.CreateFactory(decoratorType, [serviceType]);\n        var decoratorService = new ServiceDescriptor(serviceType, sp => objectFactory(sp, new[]\n        {\n            sp.CreateInstance(service)\n        }), service.Lifetime);\n\n        services.Replace(decoratorService);\n        return services;\n    }\n\n    private static object CreateInstance(this IServiceProvider services, ServiceDescriptor descriptor)\n    {\n        if (descriptor.ImplementationInstance != null)\n            return descriptor.ImplementationInstance;\n\n        if (descriptor.ImplementationFactory != null)\n            return descriptor.ImplementationFactory(services);\n\n        return ActivatorUtilities.GetServiceOrCreateInstance(services, descriptor.ImplementationType!);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/StringExtension.cs",
    "content": "﻿using System.Collections.Specialized;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\nusing System.Web;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class StringExtension\n{\n    #region Encode/Decode\n\n    /// <summary>\n    ///     Converts a string to an HTML-encoded string.\n    /// </summary>\n    /// <param name=\"s\">The string to encode.</param>\n    /// <returns>An encoded string.</returns>\n    public static string HtmlEncode(this string s)\n    {\n        return HttpUtility.HtmlEncode(s);\n    }\n\n    /// <summary>\n    ///     Converts a string that has been HTML-encoded for HTTP transmission into a decoded string.\n    /// </summary>\n    /// <param name=\"s\">The string to decode.</param>\n    /// <returns>A decoded string.</returns>\n    public static string HtmlDecode(this string s)\n    {\n        return HttpUtility.HtmlDecode(s);\n    }\n\n    /// <summary>\n    ///     Encodes a string.\n    /// </summary>\n    /// <param name=\"value\">A string to encode.</param>\n    /// <returns>An encoded string.</returns>\n    public static string JavaScriptStringEncode(this string value)\n    {\n        return HttpUtility.JavaScriptStringEncode(value);\n    }\n\n    /// <summary>\n    ///     Encodes a string.\n    /// </summary>\n    /// <param name=\"value\">A string to encode.</param>\n    /// <param name=\"addDoubleQuotes\">\n    ///     A value that indicates whether double quotation marks will be included around the\n    ///     encoded string.\n    /// </param>\n    /// <returns>An encoded string.</returns>\n    public static string JavaScriptStringEncode(this string value, bool addDoubleQuotes)\n    {\n        return HttpUtility.JavaScriptStringEncode(value, addDoubleQuotes);\n    }\n\n    /// <summary>\n    ///     Parses a query string into a  using  encoding.\n    /// </summary>\n    /// <param name=\"query\">The query string to parse.</param>\n    /// <returns>A  of query parameters and values.</returns>\n    public static NameValueCollection ParseQueryString(this string query)\n    {\n        return HttpUtility.ParseQueryString(query);\n    }\n\n    /// <summary>\n    ///     Parses a query string into a  using the specified .\n    /// </summary>\n    /// <param name=\"query\">The query string to parse.</param>\n    /// <param name=\"encoding\">The  to use.</param>\n    /// <returns>A  of query parameters and values.</returns>\n    public static NameValueCollection ParseQueryString(this string query, Encoding encoding)\n    {\n        return HttpUtility.ParseQueryString(query, encoding);\n    }\n\n    /// <summary>\n    ///     Converts a string that has been encoded for transmission in a URL into a decoded string.\n    /// </summary>\n    /// <param name=\"str\">The string to decode.</param>\n    /// <returns>A decoded string.</returns>\n    public static string UrlDecode(this string str)\n    {\n        return HttpUtility.UrlDecode(str);\n    }\n\n    /// <summary>\n    ///     Converts a URL-encoded string into a decoded string, using the specified encoding object.\n    /// </summary>\n    /// <param name=\"str\">The string to decode.</param>\n    /// <param name=\"e\">The  that specifies the decoding scheme.</param>\n    /// <returns>A decoded string.</returns>\n    public static string UrlDecode(this string str, Encoding e)\n    {\n        return HttpUtility.UrlDecode(str, e);\n    }\n\n    /// <summary>\n    ///     Encodes a URL string.\n    /// </summary>\n    /// <param name=\"str\">The text to encode.</param>\n    /// <returns>An encoded string.</returns>\n    public static string UrlEncode(this string str)\n    {\n        return HttpUtility.UrlEncode(str);\n    }\n\n    /// <summary>\n    ///     Encodes a URL string using the specified encoding object.\n    /// </summary>\n    /// <param name=\"str\">The text to encode.</param>\n    /// <param name=\"e\">The  object that specifies the encoding scheme.</param>\n    /// <returns>An encoded string.</returns>\n    public static string UrlEncode(this string str, Encoding e)\n    {\n        return HttpUtility.UrlEncode(str, e);\n    }\n\n    /// <summary>\n    /// Base64Encode with utf8 encoding\n    /// </summary>\n    /// <param name=\"str\">source string</param>\n    /// <returns>base64 encoded string</returns>\n    public static string Base64Encode(this string str) => Base64Encode(str, Encoding.UTF8);\n\n    /// <summary>\n    /// Base64Encode\n    /// </summary>\n    /// <param name=\"str\">source string</param>\n    /// <param name=\"encoding\">encoding</param>\n    /// <returns>base64 encoded string</returns>\n    public static string Base64Encode(this string str, Encoding encoding) => Convert.ToBase64String(str.GetBytes(encoding));\n\n    /// <summary>\n    /// Base64Decode with ytf8 encoding\n    /// </summary>\n    /// <param name=\"str\">base64 encoded source string</param>\n    /// <returns>base64 decoded string</returns>\n    public static string Base64Decode(this string str) => Base64Decode(str, Encoding.UTF8);\n\n    /// <summary>\n    /// Base64Decode\n    /// </summary>\n    /// <param name=\"str\">base64 encoded source string</param>\n    /// <param name=\"encoding\">encoding</param>\n    /// <returns>base64 decoded string</returns>\n    public static string Base64Decode(this string str, Encoding encoding) => Convert.FromBase64String(str).GetString(encoding);\n\n    /// <summary>\n    /// Base64UrlEncode\n    /// </summary>\n    /// <param name=\"str\">source string</param>\n    /// <returns>encoded string</returns>\n    public static string Base64UrlEncode(this string str) => Base64UrlEncodeHelper.Encode(str);\n\n    /// <summary>\n    /// Base64UrlEncode\n    /// </summary>\n    /// <param name=\"str\">base64url encoded string</param>\n    /// <returns>decode string</returns>\n    public static string Base64UrlDecode(this string str) => Base64UrlEncodeHelper.Decode(str);\n\n    #endregion Encode/Decode\n\n    /// <summary>\n    /// Get type by TypeName\n    /// Support type alias，for example: int => System.Int32\n    /// </summary>\n    /// <param name=\"typeName\">typename</param>\n    /// <returns>Type</returns>\n    [RequiresUnreferencedCode(\"The type might be removed\")]\n    public static Type GetTypeByTypeName(this string typeName)\n    {\n        var type = Guard.NotNullOrEmpty(typeName, nameof(typeName))\n                .ToLower() switch\n        {\n            \"bool\" => Type.GetType(\"System.Boolean\", true, true),\n            \"byte\" => Type.GetType(\"System.Byte\", true, true),\n            \"sbyte\" => Type.GetType(\"System.SByte\", true, true),\n            \"char\" => Type.GetType(\"System.Char\", true, true),\n            \"decimal\" => Type.GetType(\"System.Decimal\", true, true),\n            \"double\" => Type.GetType(\"System.Double\", true, true),\n            \"float\" => Type.GetType(\"System.Single\", true, true),\n            \"int\" => Type.GetType(\"System.Int32\", true, true),\n            \"uint\" => Type.GetType(\"System.UInt32\", true, true),\n            \"long\" => Type.GetType(\"System.Int64\", true, true),\n            \"ulong\" => Type.GetType(\"System.UInt64\", true, true),\n            \"object\" => Type.GetType(\"System.Object\", true, true),\n            \"short\" => Type.GetType(\"System.Int16\", true, true),\n            \"ushort\" => Type.GetType(\"System.UInt16\", true, true),\n            \"string\" => Type.GetType(\"System.String\", true, true),\n            \"datetime\" => Type.GetType(\"System.DateTime\", true, true),\n            \"guid\" => Type.GetType(\"System.Guid\", true, true),\n            _ => Type.GetType(typeName, true, true),\n        };\n        return Guard.NotNull(type);\n    }\n\n    /// <summary>\n    /// Return value if value IsNotNullOrEmpty else return defaultValue\n    /// </summary>\n    /// <param name=\"str\">string value</param>\n    /// <param name=\"defaultValue\">defaultValue</param>\n    /// <returns></returns>\n    public static string GetNotEmptyValueOrDefault(this string? str, string defaultValue)\n    {\n        return str.IsNullOrEmpty() ? defaultValue : str;\n    }\n\n    /// <summary>\n    /// Return value if value IsNotNullOrWhiteSpace else return defaultValue\n    /// </summary>\n    /// <param name=\"str\">string value</param>\n    /// <param name=\"defaultValue\">defaultValue</param>\n    /// <returns></returns>\n    public static string GetValueOrDefault(this string? str, string defaultValue)\n    {\n        return str.IsNullOrWhiteSpace() ? defaultValue : str;\n    }\n\n    /// <summary>\n    /// Return value if value IsNotNullOrWhiteSpace else return defaultValue\n    /// </summary>\n    /// <param name=\"str\">string value</param>\n    /// <param name=\"getDefault\">get defaultValue func</param>\n    /// <returns></returns>\n    public static string GetValueOrDefault(this string? str, Func<string> getDefault)\n    {\n        return str.IsNullOrWhiteSpace() ? getDefault() : str;\n    }\n\n    /// <summary>\n    /// split comma separated string to T array\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"str\">str</param>\n    /// <param name=\"splitOptions\"></param>\n    /// <returns></returns>\n    public static T[] SplitArray<T>(this string? str, StringSplitOptions splitOptions = StringSplitOptions.None) => SplitArray<T>(str, [','], splitOptions);\n\n    /// <summary>\n    /// split specific separator separated string to T array\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"str\">str</param>\n    /// <param name=\"separators\">separators</param>\n    /// <param name=\"splitOptions\">splitOptions</param>\n    /// <returns></returns>\n    public static T[] SplitArray<T>(this string? str, char[] separators, StringSplitOptions splitOptions = StringSplitOptions.None)\n    {\n        if (string.IsNullOrWhiteSpace(str))\n        {\n            return [];\n        }\n        return Guard.NotNull(str)\n            .Split(separators, splitOptions)\n            .Select(_ => _.To<T>())\n            .ToArray();\n    }\n\n    /// <summary>\n    /// remove start if start with\n    /// </summary>\n    /// <param name=\"str\">str</param>\n    /// <param name=\"start\">start to remove</param>\n    /// <returns>start removed str</returns>\n    [return: NotNullIfNotNull(nameof(str))]\n    public static string? TrimStart(this string? str, string start)\n    {\n        if (str.IsNullOrEmpty() || start.IsNullOrEmpty())\n        {\n            return str;\n        }\n        if (str.StartsWith(start))\n        {\n            str = str[start.Length..];\n        }\n        return str;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/TaskExtension.cs",
    "content": "﻿using System.Diagnostics;\nusing System.Runtime.CompilerServices;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class TaskExtension\n{\n    public static Task<T> WrapTask<T>(this T t) => Task.FromResult(t);\n\n    public static ValueTask<T> WrapValueTask<T>(this T t) => new(t);\n\n    public static Task AsTask(this CancellationToken cancellationToken)\n    {\n        var tcs = new TaskCompletionSource<object?>();\n        cancellationToken.Register(() =>\n        {\n            tcs.TrySetResult(null);\n        });\n        return tcs.Task;\n    }\n\n    public static Task WhenAny(this IEnumerable<Task> tasks) => Task.WhenAny(tasks);\n\n    public static Task<Task<TResult>> WhenAny<TResult>(this IEnumerable<Task<TResult>> tasks) => Task.WhenAny(tasks);\n\n    public static Task WhenAll(this IEnumerable<Task> tasks) => Task.WhenAll(tasks);\n\n    public static Task WhenAllSafely(this IEnumerable<Task> tasks, Action<Exception>? onException = null) => Task.WhenAll(tasks.Select(async t =>\n    {\n#if NET\n        await t.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);\n#else\n        try\n        {\n            await t;\n        }\n        catch (Exception ex)\n        {\n            onException?.Invoke(ex);\n        }\n#endif\n    }));\n\n    public static Task<TResult[]> WhenAll<TResult>(this IEnumerable<Task<TResult>> tasks) => Task.WhenAll(tasks);\n\n    #region TaskScheduler\n\n    /// <summary>\n    /// Runs an action on the current scheduler instead of the default scheduler.\n    /// </summary>\n    /// <param name=\"scheduler\">Scheduler for the action to be scheduled on.</param>\n    /// <param name=\"action\">Action to be scheduled.</param>\n    /// <param name=\"cancellationToken\">Cancellation token to link the new task to. If canceled before being scheduled, the action will not be run.</param>\n    /// <returns>New task created for the action.</returns>\n    public static Task Run(this TaskScheduler scheduler, Action action, CancellationToken cancellationToken = default)\n    {\n        var taskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, scheduler);\n        return taskFactory.StartNew(action, cancellationToken: cancellationToken);\n    }\n\n    /// <summary>\n    /// Runs a function on the current scheduler instead of the default scheduler.\n    /// </summary>\n    /// <typeparam name=\"T\">Result type.</typeparam>\n    /// <param name=\"scheduler\">Scheduler for the action to be scheduled on.</param>\n    /// <param name=\"function\">Function to be scheduled.</param>\n    /// <param name=\"cancellationToken\">Cancellation token to link the new task to. If canceled before being scheduled, the action will not be run.</param>\n    /// <returns>New task created for the function. This task completes with the result of calling the function.</returns>\n    public static Task<T> Run<T>(this TaskScheduler scheduler, Func<T> function, CancellationToken cancellationToken = default)\n    {\n        var taskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, scheduler);\n        return taskFactory.StartNew(function, cancellationToken: cancellationToken);\n    }\n\n    /// <summary>\n    /// Runs a function on the current scheduler instead of the default scheduler.\n    /// </summary>\n    /// <param name=\"scheduler\">Scheduler for the action to be scheduled on.</param>\n    /// <param name=\"function\">Function to be scheduled.</param>\n    /// <param name=\"cancelationToken\">Cancellation token to link the new task to. If canceled before being scheduled, the action will not be run.</param>\n    /// <returns>New task created for the function. This task completes with the result of the task returned by the function.</returns>\n    public static async Task Run(this TaskScheduler scheduler, Func<Task> function, CancellationToken cancelationToken = default)\n    {\n        var taskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, scheduler);\n        var innerTask = await taskFactory.StartNew(function, cancellationToken: cancelationToken);\n        await innerTask;\n    }\n\n    /// <summary>\n    /// Runs a function on the current scheduler instead of the default scheduler.\n    /// </summary>\n    /// <typeparam name=\"T\">Result type.</typeparam>\n    /// <param name=\"scheduler\">Scheduler for the action to be scheduled on.</param>\n    /// <param name=\"function\">Function to be scheduled.</param>\n    /// <param name=\"cancelationToken\">Cancellation token to link the new task to. If canceled before being scheduled, the action will not be run.</param>\n    /// <returns>New task created for the function. This task completes with the result of the task returned by the function.</returns>\n    public static async Task<T> Run<T>(this TaskScheduler scheduler, Func<Task<T>> function, CancellationToken cancelationToken = default)\n    {\n        var taskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, scheduler);\n        var innerTask = await taskFactory.StartNew(function, cancellationToken: cancelationToken);\n        return await innerTask;\n    }\n\n    #endregion TaskScheduler\n\n    public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout,\n[CallerFilePath] string? filePath = null,\n[CallerLineNumber] int lineNumber = default)\n    {\n        if (task.IsCompleted || Debugger.IsAttached)\n        {\n            return await task;\n        }\n\n        using var cts = new CancellationTokenSource();\n        if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token)))\n        {\n            cts.Cancel();\n            return await task;\n        }\n\n        throw new TimeoutException(CreateMessage(timeout, filePath, lineNumber));\n    }\n\n    public static async Task TimeoutAfter(this Task task, TimeSpan timeout,\n        [CallerFilePath] string? filePath = null,\n        [CallerLineNumber] int lineNumber = default)\n    {\n        if (task.IsCompleted || Debugger.IsAttached)\n        {\n            await task;\n            return;\n        }\n\n        using var cts = new CancellationTokenSource();\n        if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token)))\n        {\n            cts.Cancel();\n            await task;\n        }\n        else\n        {\n            throw new TimeoutException(CreateMessage(timeout, filePath, lineNumber));\n        }\n    }\n\n    private static string CreateMessage(TimeSpan timeout, string? filePath, int lineNumber)\n        => string.IsNullOrEmpty(filePath)\n        ? $\"The operation timed out after reaching the limit of {timeout.TotalMilliseconds}ms.\"\n        : $\"The operation at {filePath}:{lineNumber} timed out after reaching the limit of {timeout.TotalMilliseconds}ms.\";\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Extensions/TypeExtension.cs",
    "content": "﻿using System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Extensions;\n\npublic static class TypeExtension\n{\n    private static readonly Type[] BasicTypes =\n    [\n        typeof(bool),\n\n        typeof(sbyte),\n        typeof(byte),\n        typeof(int),\n        typeof(uint),\n        typeof(short),\n        typeof(ushort),\n        typeof(long),\n        typeof(ulong),\n        typeof(float),\n        typeof(double),\n        typeof(decimal),\n\n        typeof(Guid),\n\n        typeof(DateTime),// IsPrimitive:False\n        typeof(TimeSpan),// IsPrimitive:False\n        typeof(DateTimeOffset),\n\n        typeof(char),\n        typeof(string),// IsPrimitive:False\n\n#if NET\n        typeof(DateOnly),\n        typeof(TimeOnly),\n#endif\n\n        //typeof(object),// IsPrimitive:False\n    ];\n\n    public static TypeCode GetTypeCode(this Type type) => Type.GetTypeCode(type);\n\n    public static bool IsValueTuple(this Type type)\n            => type.IsValueType && type.FullName?.StartsWith(\"System.ValueTuple`\", StringComparison.Ordinal) == true;\n\n    /// <summary>\n    /// GetDescription\n    /// </summary>\n    /// <param name=\"type\">type</param>\n    /// <returns></returns>\n    public static string? GetDescription(this Type type) =>\n        type.GetCustomAttribute<DescriptionAttribute>()?.Description;\n\n    /// <summary>\n    /// 判断是否基元类型，如果是可空类型会先获取里面的类型，如 int? 也是基元类型\n    /// The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.\n    /// </summary>\n    /// <param name=\"type\">type</param>\n    /// <returns></returns>\n    public static bool IsPrimitiveType(this Type type)\n        => (Nullable.GetUnderlyingType(type) ?? type).IsPrimitive;\n\n    public static bool IsPrimitiveType<T>() => IsPrimitiveType(typeof(T));\n\n    public static bool IsBasicType(this Type type)\n    {\n        var unWrappedType = type.Unwrap();\n        return unWrappedType.IsEnum || BasicTypes.Contains(unWrappedType);\n    }\n\n    public static bool IsBasicType<T>() => IsBasicType(typeof(T));\n\n    public static bool HasNamespace(this Type type) => Guard.NotNull(type).Namespace != null;\n\n    /// <summary>\n    /// Finds best constructor, least parameter\n    /// </summary>\n    /// <param name=\"type\">type</param>\n    /// <param name=\"parameterTypes\"></param>\n    /// <returns>Matching constructor or default one</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static ConstructorInfo? GetConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type type, params Type[]? parameterTypes)\n    {\n        if (parameterTypes == null || parameterTypes.Length == 0)\n            return GetEmptyConstructor(type);\n\n        ActivatorHelper.FindApplicableConstructor(type, parameterTypes, out var ctor, out _);\n        return ctor;\n    }\n\n    public static ConstructorInfo? GetEmptyConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type type)\n    {\n        var constructors = type.GetConstructors();\n\n        var ctor = constructors.OrderBy(c => c.IsPublic ? 0 : (c.IsPrivate ? 2 : 1))\n            .ThenBy(c => c.GetParameters().Length).FirstOrDefault();\n\n        return ctor?.GetParameters().Length == 0 ? ctor : null;\n    }\n\n    /// <summary>\n    /// Determines whether this type is assignable to <typeparamref name=\"T\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">The type to test assignability to.</typeparam>\n    /// <param name=\"this\">The type to test.</param>\n    /// <returns>True if this type is assignable to references of type\n    /// <typeparamref name=\"T\"/>; otherwise, False.</returns>\n    public static bool IsAssignableTo<T>(this Type @this)\n    {\n        Guard.NotNull(@this);\n        return typeof(T).IsAssignableFrom(@this);\n    }\n\n    /// <summary>\n    /// Finds a constructor with the matching type parameters.\n    /// </summary>\n    /// <param name=\"type\">The type being tested.</param>\n    /// <param name=\"constructorParameterTypes\">The types of the contractor to find.</param>\n    /// <returns>The <see cref=\"ConstructorInfo\"/> is a match is found; otherwise, <c>null</c>.</returns>\n    public static ConstructorInfo? GetMatchingConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type type, Type[]? constructorParameterTypes)\n    {\n        if (constructorParameterTypes == null || constructorParameterTypes.Length == 0)\n            return GetEmptyConstructor(type);\n\n        return type.GetConstructors()\n            .FirstOrDefault(c => c.GetParameters()\n                .Select(p => p.ParameterType)\n                .SequenceEqual(constructorParameterTypes)\n            );\n    }\n\n    /// <summary>\n    /// Get ImplementedInterfaces\n    /// </summary>\n    /// <param name=\"type\">type</param>\n    /// <returns></returns>\n    public static IEnumerable<Type> GetImplementedInterfaces([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type)\n    {\n        return type.GetTypeInfo().ImplementedInterfaces;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Guard.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Runtime.CompilerServices;\n\nnamespace WeihanLi.Common;\n\npublic static class Guard\n{\n    [return: NotNull]\n    public static T NotNull<T>([NotNull] T? t,\n           [CallerArgumentExpression(nameof(t))]\n            string? paramName = default)\n    {\n#if NET\n        ArgumentNullException.ThrowIfNull(t, paramName);\n#else\n        if (t is null)\n        {\n            throw new ArgumentNullException(paramName);\n        }\n#endif\n        return t;\n    }\n\n    [return: NotNull]\n    public static string NotNullOrEmpty([NotNull] string? str,\n        [CallerArgumentExpression(nameof(str))]\n            string? paramName = null)\n    {\n#if NET\n        ArgumentException.ThrowIfNullOrEmpty(str, paramName);\n#else\n        NotNull(str, paramName);\n        if (str.Length == 0)\n        {\n            throw new ArgumentException(@\"The argument can not be Empty\", paramName);\n        }\n#endif\n        return str;\n    }\n\n    [return: NotNull]\n    public static string NotNullOrWhiteSpace([NotNull] string? str,\n        [CallerArgumentExpression(nameof(str))] string? paramName = null)\n    {\n#if NET\n        ArgumentException.ThrowIfNullOrWhiteSpace(str, paramName);\n#else\n        NotNull(str, paramName);\n        if (string.IsNullOrWhiteSpace(str))\n        {\n            throw new ArgumentException(@\"The argument can not be WhiteSpace\", paramName);\n        }\n#endif\n        return str;\n    }\n\n    [return: NotNull]\n    public static ICollection<T> NotEmpty<T>([NotNull] ICollection<T> collection, [CallerArgumentExpression(nameof(collection))] string? paramName = null)\n    {\n        NotNull(collection, paramName);\n        if (collection.Count == 0)\n        {\n            throw new ArgumentException(@\"The collection could not be empty\", paramName);\n        }\n        return collection;\n    }\n\n    public static T Ensure<T>(Func<T, bool> condition, T t, [CallerArgumentExpression(nameof(t))] string? paramName = null)\n    {\n        NotNull(condition);\n        if (!condition(t))\n        {\n            throw new ArgumentException(@\"The argument does not meet condition\", paramName);\n        }\n        return t;\n    }\n\n    public static async Task<T> EnsureAsync<T>(Func<T, Task<bool>> condition, T t, [CallerArgumentExpression(nameof(t))] string? paramName = null)\n    {\n        NotNull(condition);\n        if (!await condition(t))\n        {\n            throw new ArgumentException(@\"The argument does not meet condition\", paramName);\n        }\n        return t;\n    }\n\n    public static async Task<T> EnsureAsync<T>(Func<T, ValueTask<bool>> condition, T t, [CallerArgumentExpression(nameof(t))] string? paramName = null)\n    {\n        NotNull(condition);\n        if (!await condition(t))\n        {\n            throw new ArgumentException(@\"The argument does not meet condition\", paramName);\n        }\n        return t;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ActivatorHelper.cs",
    "content": "﻿// Copyright (c) .NET Foundation. All rights reserved.\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing System.Runtime.ExceptionServices;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic delegate object ObjectFactory(IServiceProvider serviceProvider, object?[] arguments);\n\ninternal static class ParameterDefaultValue\n{\n    private static readonly Type NullableOpenGenericType = typeof(Nullable<>);\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static bool TryGetDefaultValue(ParameterInfo parameter, out object? defaultValue)\n    {\n        bool hasDefaultValue;\n        var tryToGetDefaultValue = true;\n        defaultValue = null;\n\n        try\n        {\n            hasDefaultValue = parameter.HasDefaultValue;\n        }\n        catch (FormatException) when (parameter.ParameterType == typeof(DateTime))\n        {\n            // Workaround for https://github.com/dotnet/corefx/issues/12338\n            // If HasDefaultValue throws FormatException for DateTime\n            // we expect it to have default value\n            hasDefaultValue = true;\n            tryToGetDefaultValue = false;\n        }\n\n        if (hasDefaultValue)\n        {\n            if (tryToGetDefaultValue)\n            {\n                defaultValue = parameter.DefaultValue;\n            }\n\n            // Workaround for https://github.com/dotnet/corefx/issues/11797\n            if (defaultValue == null && parameter.ParameterType.IsValueType)\n            {\n                defaultValue = Activator.CreateInstance(parameter.ParameterType);\n            }\n\n            // Handle nullable enums\n            if (defaultValue != null &&\n                parameter.ParameterType.IsGenericType &&\n                parameter.ParameterType.GetGenericTypeDefinition() == NullableOpenGenericType\n                )\n            {\n                var underlyingType = Nullable.GetUnderlyingType(parameter.ParameterType);\n                if (underlyingType is { IsEnum: true })\n                {\n                    defaultValue = Enum.ToObject(underlyingType, defaultValue);\n                }\n            }\n        }\n\n        return hasDefaultValue;\n    }\n}\n\n/// <summary>\n/// Helper code for the various activator services.\n/// </summary>\npublic static class ActivatorHelper\n{\n    private static readonly MethodInfo GetServiceInfo =\n        GetMethodInfo<Func<IServiceProvider, Type, Type, bool, object?>>((sp, t, r, c) => GetService(sp, t, r, c));\n\n    /// <summary>\n    /// create instance of Type T with parameters\n    /// </summary>\n    /// <typeparam name=\"T\">type</typeparam>\n    /// <param name=\"parameters\">parameters</param>\n    /// <returns>T instance</returns>\n    public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(params object?[] parameters)\n    {\n        return (T)(Activator.CreateInstance(typeof(T), parameters) ?? throw new InvalidOperationException());\n    }\n\n    /// <summary>\n    /// Instantiate a type with constructor arguments provided directly and/or from an <see cref=\"IServiceProvider\"/>.\n    /// </summary>\n    /// <param name=\"provider\">The service provider used to resolve dependencies</param>\n    /// <param name=\"instanceType\">The type to activate</param>\n    /// <param name=\"parameters\">Constructor arguments not provided by the <paramref name=\"provider\"/>.</param>\n    /// <returns>An activated object of type instanceType</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static object CreateInstance(this IServiceProvider provider,\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type instanceType,\n        params object?[] parameters)\n    {\n        return MatchConstructor(instanceType, parameters).CreateInstance(provider);\n    }\n\n    /// <summary>\n    /// Match Best Constructor\n    /// </summary>\n    /// <param name=\"instanceType\">instance type to new</param>\n    /// <param name=\"parameters\">Constructor arguments not provided by di sys</param>\n    /// <returns>Best Constructor Matched</returns>\n    private static ConstructorMatcher MatchConstructor(\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]\n        Type instanceType, params object?[]? parameters)\n    {\n        parameters ??= [];\n\n        var bestLength = -1;\n\n        ConstructorMatcher bestMatcher = default;\n\n        if (!instanceType.GetTypeInfo().IsAbstract)\n        {\n            foreach (var constructor in instanceType\n                .GetTypeInfo()\n                .DeclaredConstructors)\n            {\n                if (!constructor.IsStatic && constructor.IsPublic)\n                {\n                    var matcher = new ConstructorMatcher(constructor);\n                    var length = matcher.Match(parameters);\n\n                    if (bestLength < length)\n                    {\n                        bestLength = length;\n                        bestMatcher = matcher;\n                    }\n                }\n            }\n        }\n\n        if (bestLength == -1)\n        {\n            var message = $\"A suitable constructor for type '{instanceType}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.\";\n            throw new InvalidOperationException(message);\n        }\n\n        return bestMatcher;\n    }\n\n    /// <summary>\n    /// Match Best Constructor\n    /// </summary>\n    /// <param name=\"instanceType\">instance type to new</param>\n    /// <param name=\"parameters\">Constructor arguments not provided by di sys</param>\n    /// <returns>Best Constructor Matched</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static ConstructorInfo MatchBestConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type instanceType, params object[] parameters)\n    {\n        return MatchConstructor(instanceType, parameters).Constructor;\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static object?[] GetBestConstructorArguments(IServiceProvider serviceProvider, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type instanceType, params object?[] parameters)\n    {\n        return MatchConstructor(instanceType, parameters).GetConstructorArguments(serviceProvider);\n    }\n\n    /// <summary>\n    /// Create a delegate that will instantiate a type with constructor arguments provided directly\n    /// and/or from an <see cref=\"IServiceProvider\"/>.\n    /// </summary>\n    /// <param name=\"instanceType\">The type to activate</param>\n    /// <param name=\"argumentTypes\">\n    /// The types of objects, in order, that will be passed to the returned function as its second parameter\n    /// </param>\n    /// <returns>\n    /// A factory that will instantiate instanceType using an <see cref=\"IServiceProvider\"/>\n    /// and an argument array containing objects matching the types defined in argumentTypes\n    /// </returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static ObjectFactory CreateFactory(Type instanceType, Type[] argumentTypes)\n    {\n        FindApplicableConstructor(instanceType, argumentTypes, out var constructor, out var parameterMap);\n\n        var provider = Expression.Parameter(typeof(IServiceProvider), \"provider\");\n        var argumentArray = Expression.Parameter(typeof(object[]), \"argumentArray\");\n        var factoryExpressionBody = BuildFactoryExpression(constructor!, parameterMap!, provider, argumentArray);\n\n        var factoryLambda = Expression.Lambda<Func<IServiceProvider, object?[], object>>(\n            factoryExpressionBody, provider, argumentArray);\n\n        var result = factoryLambda.Compile();\n        return result.Invoke;\n    }\n\n    /// <summary>\n    /// Instantiate a type with constructor arguments provided directly and/or from an <see cref=\"IServiceProvider\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">The type to activate</typeparam>\n    /// <param name=\"provider\">The service provider used to resolve dependencies</param>\n    /// <param name=\"parameters\">Constructor arguments not provided by the <paramref name=\"provider\"/>.</param>\n    /// <returns>An activated object of type T</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(this IServiceProvider provider, params object[] parameters)\n    {\n        return (T)CreateInstance(provider, typeof(T), parameters);\n    }\n\n    /// <summary>\n    /// Retrieve an instance of the given type from the service provider. If one is not found then instantiate it directly.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of the service</typeparam>\n    /// <param name=\"provider\">The service provider used to resolve dependencies</param>\n    /// <returns>The resolved service or created instance</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static T GetServiceOrCreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(this IServiceProvider provider)\n    {\n        return (T)GetServiceOrCreateInstance(provider, typeof(T));\n    }\n\n    /// <summary>\n    /// Retrieve an instance of the given type from the service provider. If one is not found then instantiate it directly.\n    /// </summary>\n    /// <param name=\"provider\">The service provider</param>\n    /// <param name=\"type\">The type of the service</param>\n    /// <returns>The resolved service or created instance</returns>\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static object GetServiceOrCreateInstance(this IServiceProvider provider, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type)\n    {\n        return provider.GetService(type) ?? CreateInstance(provider, type);\n    }\n\n    private static MethodInfo GetMethodInfo<T>(Expression<T> expr)\n    {\n        var mc = (MethodCallExpression)expr.Body;\n        return mc.Method;\n    }\n\n    private static object? GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)\n    {\n        var service = sp.GetService(type);\n        if (service == null && !isDefaultParameterRequired)\n        {\n            var message = $\"Unable to resolve service for type '{type}' while attempting to activate '{requiredBy}'.\";\n            throw new InvalidOperationException(message);\n        }\n        return service;\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    private static Expression BuildFactoryExpression(\n        ConstructorInfo constructor,\n        int?[] parameterMap,\n        Expression serviceProvider,\n        Expression factoryArgumentArray)\n    {\n        var constructorParameters = constructor.GetParameters();\n        var constructorArguments = new Expression[constructorParameters.Length];\n\n        for (var i = 0; i < constructorParameters.Length; i++)\n        {\n            var constructorParameter = constructorParameters[i];\n            var parameterType = constructorParameter.ParameterType;\n            var hasDefaultValue = ParameterDefaultValue.TryGetDefaultValue(constructorParameter, out var defaultValue);\n\n            if (parameterMap[i] != null)\n            {\n                constructorArguments[i] = Expression.ArrayAccess(factoryArgumentArray, Expression.Constant(parameterMap[i]));\n            }\n            else\n            {\n                var parameterTypeExpression = new[] { serviceProvider,\n                        Expression.Constant(parameterType, typeof(Type)),\n                        Expression.Constant(constructor.DeclaringType, typeof(Type)),\n                        Expression.Constant(hasDefaultValue) };\n                constructorArguments[i] = Expression.Call(GetServiceInfo, parameterTypeExpression);\n            }\n\n            // Support optional constructor arguments by passing in the default value\n            // when the argument would otherwise be null.\n            if (hasDefaultValue)\n            {\n                var defaultValueExpression = Expression.Constant(defaultValue);\n                constructorArguments[i] = Expression.Coalesce(constructorArguments[i], defaultValueExpression);\n            }\n\n            constructorArguments[i] = Expression.Convert(constructorArguments[i], parameterType);\n        }\n\n        return Expression.New(constructor, constructorArguments);\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static void FindApplicableConstructor(\n        Type instanceType,\n        Type[] argumentTypes,\n        out ConstructorInfo? matchingConstructor,\n        out int?[]? parameterMap)\n    {\n        matchingConstructor = null;\n        parameterMap = null;\n\n        if (!TryFindMatchingConstructor(instanceType, argumentTypes, ref matchingConstructor, ref parameterMap))\n        {\n            var message = $\"A suitable constructor for type '{instanceType}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.\";\n            throw new InvalidOperationException(message);\n        }\n    }\n\n    // Tries to find constructor based on provided argument types\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    private static bool TryFindMatchingConstructor(\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type instanceType,\n        Type[] argumentTypes,\n        ref ConstructorInfo? matchingConstructor,\n        ref int?[]? parameterMap)\n    {\n        foreach (var constructor in instanceType.GetTypeInfo().DeclaredConstructors)\n        {\n            if (constructor.IsStatic || !constructor.IsPublic)\n            {\n                continue;\n            }\n\n            if (TryCreateParameterMap(constructor.GetParameters(), argumentTypes, out var tempParameterMap))\n            {\n                if (matchingConstructor != null)\n                {\n                    throw new InvalidOperationException($\"Multiple constructors accepting all given argument types have been found in type '{instanceType}'. There should only be one applicable constructor.\");\n                }\n\n                matchingConstructor = constructor;\n                parameterMap = tempParameterMap;\n            }\n        }\n\n        return matchingConstructor != null;\n    }\n\n    // Creates an injective parameterMap from givenParameterTypes to assignable constructorParameters.\n    // Returns true if each given parameter type is assignable to a unique; otherwise, false.\n    private static bool TryCreateParameterMap(ParameterInfo[] constructorParameters, Type[] argumentTypes, out int?[] parameterMap)\n    {\n        parameterMap = new int?[constructorParameters.Length];\n\n        for (var i = 0; i < argumentTypes.Length; i++)\n        {\n            var foundMatch = false;\n            var givenParameter = argumentTypes[i];\n\n            for (var j = 0; j < constructorParameters.Length; j++)\n            {\n                if (parameterMap[j] != null)\n                {\n                    // This ctor parameter has already been matched\n                    continue;\n                }\n\n                if (constructorParameters[j].ParameterType.IsAssignableFrom(givenParameter))\n                {\n                    foundMatch = true;\n                    parameterMap[j] = i;\n                    break;\n                }\n            }\n\n            if (!foundMatch)\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    private readonly struct ConstructorMatcher\n    {\n        private readonly ConstructorInfo _constructor;\n        private readonly ParameterInfo[] _parameters;\n        private readonly object?[] _parameterValues;\n\n        public ConstructorMatcher(ConstructorInfo constructor)\n        {\n            Guard.NotNull(constructor, nameof(constructor));\n            _constructor = constructor;\n            _parameters = _constructor.GetParameters();\n            _parameterValues = new object?[_parameters.Length];\n        }\n\n        public int Match(object?[] givenParameters)\n        {\n            var applyIndexStart = 0;\n            var applyExactLength = 0;\n            for (var givenIndex = 0; givenIndex != givenParameters.Length; givenIndex++)\n            {\n                var givenType = givenParameters[givenIndex]?.GetType();\n                var givenMatched = false;\n\n                for (var applyIndex = applyIndexStart; givenMatched == false && applyIndex != _parameters.Length; ++applyIndex)\n                {\n                    if (_parameterValues[applyIndex] == null &&\n                        _parameters[applyIndex].ParameterType.IsAssignableFrom(givenType))\n                    {\n                        givenMatched = true;\n                        _parameterValues[applyIndex] = givenParameters[givenIndex];\n                        if (applyIndexStart == applyIndex)\n                        {\n                            applyIndexStart++;\n                            if (applyIndex == givenIndex)\n                            {\n                                applyExactLength = applyIndex;\n                            }\n                        }\n                    }\n                }\n\n                if (givenMatched == false)\n                {\n                    return -1;\n                }\n            }\n            return applyExactLength;\n        }\n\n        [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n        public object CreateInstance(IServiceProvider provider)\n        {\n            for (var index = 0; index != _parameters.Length; index++)\n            {\n                if (_parameterValues[index] == null)\n                {\n                    var value = provider.GetService(_parameters[index].ParameterType);\n                    if (value == null)\n                    {\n                        if (!ParameterDefaultValue.TryGetDefaultValue(_parameters[index], out var defaultValue))\n                        {\n                            throw new InvalidOperationException($\"Unable to resolve service for type '{_parameters[index].ParameterType}' while attempting to activate '{_constructor.DeclaringType}'.\");\n                        }\n                        else\n                        {\n                            _parameterValues[index] = defaultValue;\n                        }\n                    }\n                    else\n                    {\n                        _parameterValues[index] = value;\n                    }\n                }\n            }\n\n            try\n            {\n                return _constructor.Invoke(_parameterValues);\n            }\n            catch (TargetInvocationException ex) when (ex.InnerException != null)\n            {\n                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();\n                // The above line will always throw, but the compiler requires we throw explicitly.\n                throw;\n            }\n        }\n\n        [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n        public object?[] GetConstructorArguments(IServiceProvider provider)\n        {\n            for (var index = 0; index != _parameters.Length; index++)\n            {\n                if (_parameterValues[index] == null)\n                {\n                    var value = provider.GetService(_parameters[index].ParameterType);\n                    if (value == null)\n                    {\n                        if (!ParameterDefaultValue.TryGetDefaultValue(_parameters[index], out var defaultValue))\n                        {\n                            throw new InvalidOperationException($\"Unable to resolve service for type '{_parameters[index].ParameterType}' while attempting to activate '{_constructor.DeclaringType}'.\");\n                        }\n                        else\n                        {\n                            _parameterValues[index] = defaultValue;\n                        }\n                    }\n                    else\n                    {\n                        _parameterValues[index] = value;\n                    }\n                }\n            }\n            return _parameterValues;\n        }\n\n        public ConstructorInfo Constructor => _constructor;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ApplicationHelper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Reflection;\nusing System.Runtime;\nusing System.Runtime.InteropServices;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class ApplicationHelper\n{\n    public static string ApplicationName =>\n        Assembly.GetEntryAssembly()?.GetName().Name ?? AppDomain.CurrentDomain.FriendlyName;\n\n    public static readonly string AppRoot = AppDomain.CurrentDomain.BaseDirectory;\n\n    private static CancellationToken? _exitToken;\n    public static CancellationToken ExitToken => _exitToken ??= InvokeHelper.GetExitTokenInternal();\n\n    public static string MapPath(string virtualPath) => Path.Combine(AppRoot, virtualPath.TrimStart('~'));\n\n    /// <summary>\n    /// Get the library info from the assembly info\n    /// </summary>\n    /// <param name=\"type\">type in the assembly</param>\n    /// <returns>The assembly library info</returns>\n    public static LibraryInfo GetLibraryInfo(Type type) => GetLibraryInfo(Guard.NotNull(type).Assembly);\n\n    /// <summary>\n    /// Get the library info from the assembly info\n    /// </summary>\n    /// <param name=\"assembly\">assembly</param>\n    /// <returns>The assembly library info</returns>\n    public static LibraryInfo GetLibraryInfo(Assembly assembly)\n    {\n        Guard.NotNull(assembly);\n        var assemblyInformation = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();\n        var repositoryUrl = assembly.GetCustomAttributes<AssemblyMetadataAttribute>()\n            .FirstOrDefault(x => nameof(LibraryInfo.RepositoryUrl).Equals(x.Key))?.Value ?? string.Empty;\n        if (assemblyInformation is not null)\n        {\n            var informationalVersionSplit = assemblyInformation.InformationalVersion\n#if NET\n                .Split('+', 2)\n#else\n                .Split('+')\n#endif\n                ;\n            return new LibraryInfo()\n            {\n                LibraryFullVersion = assemblyInformation.InformationalVersion,\n                LibraryVersion = informationalVersionSplit[0],\n                LibraryHash = informationalVersionSplit.Length > 1 ? informationalVersionSplit[1] : string.Empty,\n                RepositoryUrl = repositoryUrl\n            };\n        }\n        return new LibraryInfo()\n        {\n            LibraryVersion = assembly.GetName().Version?.ToString() ?? string.Empty,\n            LibraryHash = string.Empty,\n            RepositoryUrl = repositoryUrl\n        };\n    }\n\n    private static readonly Lazy<RuntimeInfo> LazyRuntimeInfo = new(GetRuntimeInfo);\n    public static RuntimeInfo RuntimeInfo => LazyRuntimeInfo.Value;\n\n    /// <summary>\n    /// Get dotnet executable path\n    /// </summary>\n    public static string? GetDotnetPath()\n    {\n        var environmentOverride = Environment.GetEnvironmentVariable(\"DOTNET_ROOT\");\n        if (!string.IsNullOrEmpty(environmentOverride) && Directory.Exists(environmentOverride))\n        {\n            var execFileName =\n#if NET\n                OperatingSystem.IsWindows()\n#else\n                RuntimeInformation.IsOSPlatform(OSPlatform.Windows)\n#endif\n                ? \"dotnet.exe\"\n                : \"dotnet\"\n                ;\n            var dotnetExePath = Path.Combine(environmentOverride, execFileName);\n            if (File.Exists(dotnetExePath))\n                return dotnetExePath;\n\n            throw new InvalidOperationException($\"dotnet executable file not found under specified DOTNET_ROOT {environmentOverride}\");\n        }\n        return ResolvePath(\"dotnet\");\n    }\n\n    public static string GetDotnetDirectory()\n    {\n        var dotnetExe = GetDotnetPath();\n\n        if (dotnetExe.IsNotNullOrEmpty() && !InteropHelper.RunningOnWindows)\n        {\n            // e.g. on Linux the 'dotnet' command from PATH is a symbol link so we need to\n            // resolve it to get the actual path to the binary\n            dotnetExe = InteropHelper.Unix.RealPath(dotnetExe) ?? dotnetExe;\n        }\n\n        if (string.IsNullOrWhiteSpace(dotnetExe))\n        {\n#if NET\n            dotnetExe = Environment.ProcessPath;\n#else\n            dotnetExe = System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName;\n#endif\n        }\n\n        return Guard.NotNull(Path.GetDirectoryName(dotnetExe));\n    }\n\n    public static string? ResolvePath(string execName) => ResolvePath(execName, \".exe\");\n\n    public static string? ResolvePath(string execName, string? windowsExt)\n    {\n        var executableName = execName;\n        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)\n            && !Path.HasExtension(execName)\n            && !string.IsNullOrEmpty(windowsExt)\n            )\n        {\n            executableName = $\"{executableName}{windowsExt}\";\n        }\n        var searchPaths = Guard.NotNull(Environment.GetEnvironmentVariable(\"PATH\"))\n            .Split(new[] { Path.PathSeparator }, options: StringSplitOptions.RemoveEmptyEntries)\n            .Select(p => p.Trim('\"'))\n            .ToArray();\n        var commandPath = searchPaths\n            .Where(p => !Path.GetInvalidPathChars().Any(p.Contains))\n            .Select(p => Path.Combine(p, executableName))\n            .FirstOrDefault(File.Exists);\n        return commandPath;\n    }\n\n    private static RuntimeInfo GetRuntimeInfo()\n    {\n        var libInfo = GetLibraryInfo(typeof(object).Assembly);\n#if NET\n#else\n        var currentProcess = System.Diagnostics.Process.GetCurrentProcess();\n#endif\n        var runtimeInfo = new RuntimeInfo()\n        {\n            Version = Environment.Version.ToString(),\n\n#if NET\n            ProcessId = Environment.ProcessId,\n            ProcessPath = Environment.ProcessPath ?? string.Empty,\n            RuntimeIdentifier = RuntimeInformation.RuntimeIdentifier,\n#else\n            ProcessId = currentProcess.Id,\n            ProcessPath = currentProcess.MainModule?.FileName ?? string.Empty,\n#endif\n\n            ProcessorCount = Environment.ProcessorCount,\n            FrameworkDescription = RuntimeInformation.FrameworkDescription,\n            WorkingDirectory = Environment.CurrentDirectory,\n            OSArchitecture = RuntimeInformation.OSArchitecture.ToString(),\n            OSDescription = RuntimeInformation.OSDescription,\n            OSVersion = Environment.OSVersion.ToString(),\n            MachineName = Environment.MachineName,\n            UserName = Environment.UserName,\n\n            IsServerGC = GCSettings.IsServerGC,\n\n            IsInContainer = IsInContainer(),\n            IsInKubernetes = IsInKubernetesCluster(),\n            KubernetesNamespace = GetKubernetesNamespace(),\n\n            LibraryInfo = libInfo,\n        };\n        return runtimeInfo;\n    }\n\n    #region ContainerEnvironment\n    // container environment\n    // https://github.com/dotnet/dotnet-docker/blob/d90d458deada9057d7889f76d58fc0a7194a0c06/src/runtime-deps/6.0/alpine3.20/amd64/Dockerfile#L7\n\n    /// <summary>\n    /// Whether running inside a container\n    /// </summary>\n    private static bool IsInContainer()\n    {\n        return \"true\".Equals(Environment.GetEnvironmentVariable(\"DOTNET_RUNNING_IN_CONTAINER\"),\n            StringComparison.OrdinalIgnoreCase);\n    }\n\n    // Kubernetes environment\n    // https://github.com/kubernetes-client/csharp/blob/36a02046439d01f1256aed4e5071cb7f1b57d6eb/src/KubernetesClient/KubernetesClientConfiguration.InCluster.cs#L41\n    private static readonly string ServiceAccountPath =\n        Path.Combine(\n        [\n            $\"{Path.DirectorySeparatorChar}var\", \"run\", \"secrets\", \"kubernetes.io\", \"serviceaccount\",\n        ]);\n    private const string ServiceAccountTokenKeyFileName = \"token\";\n    private const string ServiceAccountRootCAKeyFileName = \"ca.crt\";\n    private const string ServiceAccountNamespaceFileName = \"namespace\";\n\n    /// <summary>\n    /// Whether running inside a k8s cluster\n    /// </summary>\n    /// <returns></returns>\n    private static bool IsInKubernetesCluster()\n    {\n        var host = Environment.GetEnvironmentVariable(\"KUBERNETES_SERVICE_HOST\");\n        var port = Environment.GetEnvironmentVariable(\"KUBERNETES_SERVICE_PORT\");\n\n        if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(port))\n        {\n            return false;\n        }\n\n        var tokenPath = Path.Combine(ServiceAccountPath, ServiceAccountTokenKeyFileName);\n        if (!File.Exists(tokenPath))\n        {\n            return false;\n        }\n        var certPath = Path.Combine(ServiceAccountPath, ServiceAccountRootCAKeyFileName);\n        return File.Exists(certPath);\n    }\n\n    /// <summary>\n    /// Get Kubernetes namespace\n    /// </summary>\n    /// <returns>The namespace current workload in</returns>\n    private static string? GetKubernetesNamespace()\n    {\n        var namespaceFilePath = Path.Combine(ServiceAccountPath, ServiceAccountNamespaceFileName);\n        return File.Exists(namespaceFilePath) ? File.ReadAllText(namespaceFilePath).Trim() : null;\n    }\n    #endregion ContainerEnvironment\n}\n\npublic class LibraryInfo\n{\n    private readonly string? _fullVersion;\n    public required string LibraryVersion { get; init; }\n    public required string LibraryHash { get; init; }\n    public string LibraryFullVersion { get => _fullVersion ?? LibraryVersion; init => _fullVersion = value; }\n    public required string RepositoryUrl { get; init; }\n}\n\npublic class RuntimeInfo\n{\n    public required string Version { get; init; }\n    public required string FrameworkDescription { get; init; }\n    public required int ProcessorCount { get; init; }\n    public required string OSArchitecture { get; init; }\n    public required string OSDescription { get; init; }\n    public required string OSVersion { get; init; }\n    public required string MachineName { get; init; }\n    public required string UserName { get; init; }\n\n#if NET\n    public required string RuntimeIdentifier { get; init; }\n#endif\n\n    // GC\n    /// <summary>Gets a value that indicates whether server garbage collection is enabled.</summary>\n    /// <returns>\n    /// <see langword=\"true\" /> if server garbage collection is enabled; otherwise, <see langword=\"false\" />.</returns>\n    public required bool IsServerGC { get; init; }\n\n    public required string WorkingDirectory { get; init; }\n    public required int ProcessId { get; init; }\n    public required string ProcessPath { get; init; }\n\n    /// <summary>\n    /// Is running in a container\n    /// </summary>\n    public required bool IsInContainer { get; init; }\n\n    /// <summary>\n    /// Is running in a Kubernetes cluster\n    /// </summary>\n    public required bool IsInKubernetes { get; init; }\n\n    /// <summary>\n    /// Kubernetes namespace when running in a Kubernetes cluster\n    /// </summary>\n    public string? KubernetesNamespace { get; init; }\n\n    /// <summary>\n    /// Runtime library info\n    /// </summary>\n    public required LibraryInfo LibraryInfo { get; init; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ArrayHelper.cs",
    "content": "﻿namespace WeihanLi.Common.Helpers;\n\npublic static class ArrayHelper\n{\n    public static T[] Empty<T>() => System.Array.Empty<T>();\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/AsyncLock.cs",
    "content": "﻿namespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// AsyncLock basedOn SemaphoreSlim\n/// </summary>\npublic sealed class AsyncLock : IDisposable\n{\n    private readonly SemaphoreSlim _mutex = new(1, 1);\n\n    public IDisposable Lock()\n    {\n        _mutex.Wait();\n        return new AsyncLockReleaser(_mutex);\n    }\n\n    public Task<IDisposable> LockAsync() => LockAsync(CancellationToken.None);\n\n    public Task<IDisposable> LockAsync(CancellationToken cancellationToken) => LockAsync(TimeSpan.Zero, cancellationToken);\n\n    public async Task<IDisposable> LockAsync(TimeSpan timeout, CancellationToken cancellationToken)\n    {\n        if (timeout <= TimeSpan.Zero)\n        {\n            await _mutex.WaitAsync(cancellationToken);\n        }\n        else\n        {\n            await _mutex.WaitAsync(timeout, cancellationToken);\n        }\n        return new AsyncLockReleaser(_mutex);\n    }\n\n    public void Dispose()\n    {\n        _mutex.Dispose();\n    }\n\n    #region AsyncLockReleaser\n\n    private readonly struct AsyncLockReleaser(SemaphoreSlim semaphoreSlim) : IDisposable\n    {\n        private readonly SemaphoreSlim _semaphoreSlim = semaphoreSlim;\n\n        public void Dispose()\n        {\n            _semaphoreSlim.Release();\n        }\n    }\n\n    #endregion AsyncLockReleaser\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Base32EncodeHelper.cs",
    "content": "﻿using WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// Base32Encode\n/// https://stackoverflow.com/questions/641361/base32-decoding/7135008#7135008\n/// </summary>\npublic static class Base32EncodeHelper\n{\n    /// <summary>\n    /// Standard Base32 characters\n    /// https://www.rfc-editor.org/rfc/rfc4648#section-6\n    /// </summary>\n    public static readonly char[] Characters = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\".ToCharArray();\n\n    public static byte[] GetBytes(string base32String, char paddingChar = '=')\n    {\n        base32String = Guard.NotNullOrEmpty(base32String).TrimEnd(paddingChar); //remove padding characters\n        var byteCount = base32String.Length * 5 / 8; //this must be TRUNCATED\n        var returnArray = new byte[byteCount];\n\n        byte curByte = 0, bitsRemaining = 8;\n        var arrayIndex = 0;\n        foreach (var c in base32String)\n        {\n            var cValue = CharToValue(c);\n\n            int mask;\n            if (bitsRemaining > 5)\n            {\n                mask = cValue << (bitsRemaining - 5);\n                curByte = (byte)(curByte | mask);\n                bitsRemaining -= 5;\n            }\n            else\n            {\n                mask = cValue >> (5 - bitsRemaining);\n                curByte = (byte)(curByte | mask);\n                returnArray[arrayIndex++] = curByte;\n                curByte = (byte)(cValue << (3 + bitsRemaining));\n                bitsRemaining += 3;\n            }\n        }\n\n        //if we didn't end with a full byte\n        if (arrayIndex != byteCount)\n        {\n            returnArray[arrayIndex] = curByte;\n        }\n\n        return returnArray;\n    }\n\n    public static string FromBytes(byte[] base32Bytes, char paddingChar = '=')\n    {\n        if (base32Bytes.IsNullOrEmpty())\n        {\n            return string.Empty;\n        }\n\n        var charCount = (int)Math.Ceiling(base32Bytes.Length / 5d) * 8;\n        var returnArray = new char[charCount];\n\n        byte nextChar = 0, bitsRemaining = 5;\n        var arrayIndex = 0;\n\n        foreach (var b in base32Bytes)\n        {\n            nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));\n            returnArray[arrayIndex++] = ValueToChar(nextChar);\n\n            if (bitsRemaining < 4)\n            {\n                nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);\n                returnArray[arrayIndex++] = ValueToChar(nextChar);\n                bitsRemaining += 5;\n            }\n\n            bitsRemaining -= 3;\n            nextChar = (byte)((b << bitsRemaining) & 31);\n        }\n\n        //if we didn't end with a full char\n        if (arrayIndex != charCount)\n        {\n            returnArray[arrayIndex++] = ValueToChar(nextChar);\n            while (arrayIndex != charCount) returnArray[arrayIndex++] = paddingChar; //padding\n        }\n\n        return new string(returnArray);\n    }\n\n    private static int CharToValue(char c)\n    {\n        var value = (int)c;\n        return value switch\n        {\n            //65-90 == uppercase letters\n            < 91 and > 64 => value - 65,\n            //50-55 == numbers 2-7\n            < 56 and > 49 => value - 24,\n            //97-122 == lowercase letters\n            < 123 and > 96 => value - 97,\n            _ => throw new ArgumentException(@\"Character is not a Base32 character.\", nameof(c))\n        };\n    }\n\n    private static char ValueToChar(byte b)\n    {\n        return b switch\n        {\n            < 26 => (char)(b + 65),\n            < 32 => (char)(b + 24),\n            _ => throw new ArgumentException(@\"The byte is not a Base32 value.\", nameof(b))\n        };\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Base64UrlEncodeHelper.cs",
    "content": "﻿using System.Text;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class Base64UrlEncodeHelper\n{\n    private const char Base64PadCharacter = '=';\n    private const string DoubleBase64PadCharacter = \"==\";\n    private const char Base64Character62 = '+';\n    private const char Base64Character63 = '/';\n    private const char Base64UrlCharacter62 = '-';\n    private const char Base64UrlCharacter63 = '_';\n\n    /// <summary>\n    /// The following functions perform base64url encoding which differs from regular base64 encoding as follows\n    /// * padding is skipped so the pad character '=' doesn't have to be percent encoded\n    /// * the 62nd and 63rd regular base64 encoding characters ('+' and '/') are replace with ('-' and '_')\n    /// The changes make the encoding alphabet file and URL safe.\n    /// </summary>\n    /// <param name=\"arg\">string to encode.</param>\n    /// <returns>Base64Url encoding of the UTF8 bytes.</returns>\n    public static string Encode(string arg)\n    {\n        return Encode(Guard.NotNull(arg).GetBytes());\n    }\n\n    /// <summary>\n    /// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify\n    /// the subset as an offset in the input array, and the number of elements in the array to convert.\n    /// </summary>\n    /// <param name=\"inArray\">An array of 8-bit unsigned integers.</param>\n    /// <param name=\"length\">An offset in inArray.</param>\n    /// <param name=\"offset\">The number of elements of inArray to convert.</param>\n    /// <returns>The string representation in base 64 url encoding of length elements of inArray, starting at position offset.</returns>\n    /// <exception cref=\"T:System.ArgumentNullException\">'inArray' is null.</exception>\n    /// <exception cref=\"T:System.ArgumentOutOfRangeException\">offset or length is negative OR offset plus length is greater than the length of inArray.</exception>\n    public static string Encode(byte[] inArray, int offset, int length)\n    {\n        Guard.NotNull(inArray);\n        return Convert.ToBase64String(inArray, offset, length).Split(Base64PadCharacter)[0].Replace(Base64Character62, Base64UrlCharacter62).Replace(Base64Character63, Base64UrlCharacter63);\n    }\n\n    /// <summary>\n    /// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify\n    /// the subset as an offset in the input array, and the number of elements in the array to convert.\n    /// </summary>\n    /// <param name=\"inArray\">An array of 8-bit unsigned integers.</param>\n    /// <returns>The string representation in base 64 url encoding of length elements of inArray, starting at position offset.</returns>\n    /// <exception cref=\"T:System.ArgumentNullException\">'inArray' is null.</exception>\n    /// <exception cref=\"T:System.ArgumentOutOfRangeException\">offset or length is negative OR offset plus length is greater than the length of inArray.</exception>\n    public static string Encode(byte[] inArray)\n    {\n        Guard.NotNull(inArray);\n        return Convert.ToBase64String(inArray, 0, inArray.Length).Split(Base64PadCharacter)[0].Replace(Base64Character62, Base64UrlCharacter62).Replace(Base64Character63, Base64UrlCharacter63);\n    }\n\n    /// <summary>\n    /// Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array.</summary>\n    /// <param name=\"str\">base64Url encoded string.</param>\n    /// <returns>UTF8 bytes.</returns>\n    public static byte[] DecodeBytes(string str)\n    {\n        Guard.NotNullOrEmpty(str);\n        str = str.Replace(Base64UrlCharacter62, Base64Character62);\n        str = str.Replace(Base64UrlCharacter63, Base64Character63);\n        switch (str.Length % 4)\n        {\n            case 0:\n                return Convert.FromBase64String(str);\n\n            case 2:\n                str += DoubleBase64PadCharacter;\n                goto case 0;\n            case 3:\n                str += Base64PadCharacter.ToString();\n                goto case 0;\n            default:\n                throw new FormatException($\"IDX10400: Unable to decode: '{str}' as Base64url encoded string.\");\n        }\n    }\n\n    /// <summary>Decodes the string from Base64UrlEncoded to UTF8.</summary>\n    /// <param name=\"arg\">string to decode.</param>\n    /// <returns>UTF8 string.</returns>\n    public static string Decode(string arg)\n    {\n        return Encoding.UTF8.GetString(DecodeBytes(arg));\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/BoundedConcurrentQueue.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic sealed class BoundedConcurrentQueue<T>\n{\n    private const int NonBounded = -1;\n    private readonly ConcurrentQueue<T> _queue = new();\n    private readonly int _queueLimit;\n    private readonly BoundedQueueFullMode _mode;\n    private int _counter;\n\n    public BoundedConcurrentQueue()\n    {\n        _queueLimit = NonBounded;\n        _mode = BoundedQueueFullMode.DropWrite;\n    }\n\n    public BoundedConcurrentQueue(int queueLimit, BoundedQueueFullMode mode = BoundedQueueFullMode.DropWrite)\n    {\n        if (queueLimit <= 0)\n            throw new ArgumentOutOfRangeException(nameof(queueLimit), Resource.ValueMustBePositive);\n\n        _queueLimit = queueLimit;\n        _mode = mode;\n    }\n\n    public int Count => _queue.Count;\n\n    public bool TryDequeue([MaybeNullWhen(false)] out T item)\n    {\n        if (_queueLimit == NonBounded)\n            return _queue.TryDequeue(out item);\n\n        var result = false;\n\n        if (_queue.TryDequeue(out item))\n        {\n            result = true;\n            Interlocked.Decrement(ref _counter);\n        }\n\n        return result;\n    }\n\n    public bool TryEnqueue(T item)\n    {\n        if (_queueLimit == NonBounded)\n        {\n            _queue.Enqueue(item);\n            return true;\n        }\n\n        var result = true;\n\n        if (Interlocked.Increment(ref _counter) <= _queueLimit)\n        {\n            _queue.Enqueue(item);\n        }\n        else\n        {\n            if (_mode == BoundedQueueFullMode.DropOldest)\n            {\n                while (Interlocked.Decrement(ref _counter) >= _queueLimit)\n                {\n                    _queue.TryDequeue(out _);\n                }\n                _queue.Enqueue(item);\n                result = true;\n            }\n            else\n            {\n                Interlocked.Decrement(ref _counter);\n                result = false;\n            }\n        }\n\n        return result;\n    }\n\n    public T[] ToArray() => _queue.ToArray();\n}\n\npublic enum BoundedQueueFullMode\n{\n    DropWrite = 0,\n    DropOldest = 1\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/BuildProcess.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Diagnostics;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic interface IBuildProcessBuilder\n{\n    IBuildProcessBuilder WithTask(string name, Action<IBuildTaskBuilder> buildTaskConfigure);\n\n    IBuildProcessBuilder WithSetup(Func<Task> setupFunc);\n\n    IBuildProcessBuilder WithCleanup(Func<Task> cleanupFunc);\n\n    IBuildProcessBuilder WithCancelled(Func<Task> cancelledFunc);\n\n    IBuildProcessBuilder WithTaskExecuting(Func<IBuildTaskDescriptor, Task> taskExecutingAction);\n\n    IBuildProcessBuilder WithTaskExecuted(Func<IBuildTaskDescriptor, Task> taskExecutedAction);\n}\n\npublic interface IBuildTaskDescriptor\n{\n    string Name { get; }\n    string Description { get; }\n}\n\npublic interface IBuildTaskBuilder\n{\n    IBuildTaskBuilder WithDescription(string? description);\n    IBuildTaskBuilder WithDependency(string dependencyTaskName);\n    IBuildTaskBuilder WithExecution(Func<CancellationToken, Task> execution);\n}\n\npublic static class BuildProcessExtensions\n{\n    extension(IBuildProcessBuilder builder)\n    {\n        public IBuildProcessBuilder WithSetup(Action setupAction) => builder.WithSetup(setupAction.WrapTask());\n        public IBuildProcessBuilder WithCleanup(Action cleanupAction) => builder.WithCleanup(cleanupAction.WrapTask());\n        public IBuildProcessBuilder WithCancelled(Action cancelledAction) => builder.WithCancelled(cancelledAction.WrapTask());\n        public IBuildProcessBuilder WithTaskExecuting(Action<IBuildTaskDescriptor> taskExecutingAction)\n            => builder.WithTaskExecuting(taskExecutingAction.WrapTask());\n        public IBuildProcessBuilder WithTaskExecuted(Action<IBuildTaskDescriptor> taskExecutedAction)\n            => builder.WithTaskExecuted(taskExecutedAction.WrapTask());\n        public IBuildProcessBuilder WithTaskExecution(string name, Action taskExecution)\n            => builder.WithTask(name, taskBuilder => taskBuilder.WithExecution(taskExecution));\n        public IBuildProcessBuilder WithTaskExecution(string name, Func<Task> taskExecution)\n            => builder.WithTask(name, taskBuilder => taskBuilder.WithExecution(taskExecution));\n        public IBuildProcessBuilder WithTaskExecution(string name, Func<CancellationToken, Task> taskExecution)\n            => builder.WithTask(name, taskBuilder => taskBuilder.WithExecution(taskExecution));\n    }\n\n    extension(IBuildTaskBuilder builder)\n    {\n        public IBuildTaskBuilder WithExecution(Action executionAction) => builder.WithExecution(executionAction.WrapTask());\n        public IBuildTaskBuilder WithExecution(Func<Task> executionFunc) => builder.WithExecution(executionFunc.WrapCancellation());\n    }\n\n    extension(BuildProcess)\n    {\n        public static IBuildProcessBuilder CreateBuilder() => new BuildProcessBuilder();\n    }\n\n    extension(DotNetBuildProcessOptions options)\n    {\n        public DotNetBuildProcessOptions WithTaskExecution(string name, Action taskExecution)\n            => options.WithTaskExecution(name, taskExecution.WrapTask());\n        public DotNetBuildProcessOptions WithTaskExecution(string name, Func<Task> taskExecution)\n            => options.WithTaskExecution(name, taskExecution.WrapCancellation());\n    }\n}\n\npublic sealed class BuildProcess(IReadOnlyCollection<BuildTask> tasks,\n    Func<Task>? setup = null, Func<Task>? cleanup = null, Func<Task>? cancelled = null,\n    Func<IBuildTaskDescriptor, Task>? taskExecuting = null, Func<IBuildTaskDescriptor, Task>? taskExecuted = null)\n{\n    private readonly IReadOnlyCollection<BuildTask> _tasks = tasks;\n    private readonly Func<Task>? _setup = setup, _cleanup = cleanup, _cancelled = cancelled;\n    private readonly Func<IBuildTaskDescriptor, Task>? _taskExecuting = taskExecuting, _taskExecuted = taskExecuted;\n\n    public async Task ExecuteAsync(string target, CancellationToken cancellationToken = default)\n    {\n        var task = _tasks.FirstOrDefault(x => x.Name == target);\n        if (task is null)\n            throw new InvalidOperationException(\"Invalid target to execute\");\n\n        try\n        {\n            if (_setup != null)\n                await _setup.Invoke();\n\n            await ExecuteTask(task, cancellationToken);\n        }\n        catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)\n        {\n            if (_cancelled != null)\n                await _cancelled.Invoke();\n        }\n        finally\n        {\n            if (_cleanup != null)\n                await _cleanup.Invoke();\n        }\n    }\n\n    private async Task ExecuteTask(BuildTask task, CancellationToken cancellationToken)\n    {\n        foreach (var dependencyTask in task.Dependencies)\n        {\n            await ExecuteTask(dependencyTask, cancellationToken);\n        }\n\n        if (_taskExecuting != null)\n            await _taskExecuting.Invoke(task);\n        await task.ExecuteAsync(cancellationToken);\n        if (_taskExecuted != null)\n            await _taskExecuted.Invoke(task);\n    }\n}\n\npublic sealed class BuildTask(string name, string? description, Func<CancellationToken, Task>? execution = null)\n    : IBuildTaskDescriptor\n{\n    private IReadOnlyCollection<BuildTask> _dependencies = [];\n\n    public string Name => name;\n    public string Description => description ?? name;\n    public IReadOnlyCollection<BuildTask> Dependencies => _dependencies;\n\n    internal void SetDependencies(IReadOnlyCollection<BuildTask> dependencies)\n    {\n        _dependencies = dependencies;\n    }\n\n    public Task ExecuteAsync(CancellationToken cancellationToken) =>\n        execution?.Invoke(cancellationToken) ?? Task.CompletedTask;\n}\n\ninternal sealed class BuildTaskBuilder(string name) : IBuildTaskBuilder\n{\n    private readonly string _name = name;\n\n    private string? _description;\n    private Func<CancellationToken, Task>? _execution;\n    private readonly List<string> _dependencies = [];\n\n    public IBuildTaskBuilder WithDescription(string? description)\n    {\n        _description = description;\n        return this;\n    }\n\n    public IBuildTaskBuilder WithExecution(Func<CancellationToken, Task> execution)\n    {\n        _execution = execution;\n        return this;\n    }\n\n    public IBuildTaskBuilder WithDependency(string dependencyTaskName)\n    {\n        if (string.IsNullOrWhiteSpace(dependencyTaskName))\n            throw new ArgumentException(@\"Dependency task name could not be null or whitespace\", nameof(dependencyTaskName));\n\n        _dependencies.Add(dependencyTaskName);\n        return this;\n    }\n\n    internal IReadOnlyCollection<string> DependencyNames => _dependencies;\n\n    internal BuildTask Build()\n    {\n        return new BuildTask(_name, _description, _execution);\n    }\n}\n\ninternal sealed class BuildProcessBuilder : IBuildProcessBuilder\n{\n    private readonly Dictionary<string, Action<BuildTaskBuilder>> _taskBuilders = new(StringComparer.Ordinal);\n    private Func<Task>? _setup, _cleanup, _cancelled;\n    private Func<IBuildTaskDescriptor, Task>? _taskExecuting, _taskExecuted;\n\n    public IBuildProcessBuilder WithTask(string name, Action<IBuildTaskBuilder> buildTaskConfigure)\n    {\n        if (string.IsNullOrWhiteSpace(name))\n            throw new ArgumentException(@\"Task name could not be null or whitespace\", nameof(name));\n\n        if (_taskBuilders.TryGetValue(name, out var taskBuilder))\n        {\n\n            _taskBuilders[name] = taskBuilder + buildTaskConfigure;\n        }\n        else\n        {\n            _taskBuilders[name] = buildTaskConfigure;\n        }\n\n        return this;\n    }\n\n    public IBuildProcessBuilder WithSetup(Func<Task> setupFunc)\n    {\n        _setup = setupFunc;\n        return this;\n    }\n\n    public IBuildProcessBuilder WithCleanup(Func<Task> cleanupFunc)\n    {\n        _cleanup = cleanupFunc;\n        return this;\n    }\n\n    public IBuildProcessBuilder WithCancelled(Func<Task> cancelledFunc)\n    {\n        _cancelled = cancelledFunc;\n        return this;\n    }\n\n    public IBuildProcessBuilder WithTaskExecuting(Func<IBuildTaskDescriptor, Task> taskExecutingAction)\n    {\n        _taskExecuting = taskExecutingAction;\n        return this;\n    }\n\n    public IBuildProcessBuilder WithTaskExecuted(Func<IBuildTaskDescriptor, Task> taskExecutedAction)\n    {\n        _taskExecuted = taskExecutedAction;\n        return this;\n    }\n\n    public BuildProcess Build()\n    {\n        if (_taskBuilders.Count == 0)\n        {\n            throw new InvalidOperationException(\"No tasks configured\");\n        }\n\n        var tasksDictionary = new Dictionary<string, (BuildTaskBuilder TaskBuilder, BuildTask Task)>(StringComparer.Ordinal);\n        foreach (var taskName in _taskBuilders.Keys)\n        {\n            var taskBuilder = new BuildTaskBuilder(taskName);\n            _taskBuilders[taskName].Invoke(taskBuilder);\n            tasksDictionary[taskName] = (taskBuilder, taskBuilder.Build());\n        }\n\n        foreach (var taskName in _taskBuilders.Keys)\n        {\n            var builder = tasksDictionary[taskName];\n            var dependencies = builder.TaskBuilder.DependencyNames.Select(name =>\n            {\n                if (!tasksDictionary.TryGetValue(name, out var dependency))\n                {\n                    throw new InvalidOperationException($\"No task found with name {name}\");\n                }\n\n                return dependency.Task;\n            }).ToArray();\n\n            tasksDictionary[taskName].Task.SetDependencies(dependencies);\n        }\n\n        var tasks = tasksDictionary.Values.Select(t => t.Task).ToArray();\n        return new BuildProcess(tasks, _setup, _cleanup, _cancelled, _taskExecuting, _taskExecuted);\n    }\n}\n\npublic sealed class DotNetBuildProcessOptions\n{\n    public string? SolutionPath { get; set; }\n    public string[]? SrcProjects { get; set; }\n    public string[]? TestProjects { get; set; }\n    public string[]? RunFileSampleFolders { get; set; }\n    public Func<string?> FallbackNuGetApiKeyFunc { get; set; } = () => EnvHelper.Val(\"NuGet__ApiKey\");\n    public Func<string?> FallbackNuGetSourceFunc { get; set; } = () => EnvHelper.Val(\"NuGet__Source\");\n    public string ArtifactsPath { get; set; } = \"./artifacts/dist\";\n    public Func<string?> BranchFunc { get; set; } = () => EnvHelper.Val(\"BUILD_SOURCEBRANCHNAME\", EnvHelper.Val(\"GITHUB_REF_NAME\"));\n    public bool AllowLocalPush { get; set; }\n\n    internal readonly Dictionary<string, Action<IBuildTaskBuilder>> TaskOverride = new();\n\n    public DotNetBuildProcessOptions WithTaskConfigure(string name, Action<IBuildTaskBuilder> taskConfigure)\n    {\n        TaskOverride[name] = taskConfigure;\n        return this;\n    }\n\n    public DotNetBuildProcessOptions WithTaskExecution(string name, Func<CancellationToken, Task> taskExecution)\n    {\n        TaskOverride[name] = x => x.WithExecution(taskExecution);\n        return this;\n    }\n}\n\npublic sealed class DotNetPackageBuildProcess\n{\n    private readonly BuildProcess _buildProcess;\n    private string? _apiKey, _source, _branch;\n    private bool _stable, _noPush;\n\n    private DotNetPackageBuildProcess(DotNetBuildProcessOptions options)\n    {\n        _branch = options.BranchFunc();\n        var builder = BuildProcess.CreateBuilder()\n            .WithSetup(() =>\n            {\n                // cleanup artifacts\n                if (Directory.Exists(options.ArtifactsPath))\n                    Directory.Delete(options.ArtifactsPath, true);\n\n                // args\n                Console.WriteLine(@\"Executing command line:\");\n                ConsoleHelper.WriteLineWithColor($@\"  {Environment.CommandLine}\", ConsoleColor.DarkGreen);\n                Console.WriteLine($@\"Branch: {_branch}, stable: {_stable}\");\n            })\n            .WithTaskExecuting(task => ConsoleHelper.WriteLineWithColor($@\"===== Task {task.Name} {task.Description} executing ======\", ConsoleColor.DarkCyan))\n            .WithTaskExecuted(task => ConsoleHelper.WriteLineWithColor($@\"===== Task {task.Name} {task.Description} executed ======\", ConsoleColor.DarkGreen))\n            .WithTask(\"build\", b =>\n            {\n                b.WithDescription(\"dotnet build\")\n                  .WithExecution(() =>\n                  {\n                      Console.WriteLine($@\"Build solution {options.SolutionPath}\");\n                      CommandExecutor.ExecuteCommandAndOutput($\"dotnet build {options.SolutionPath}\")\n                          .EnsureSuccessExitCode();\n\n                      if (options.RunFileSampleFolders is not { Length: > 0 })\n                          return;\n\n                      Console.WriteLine(@\"RunFileSamples: \" + string.Join(\", \", options.RunFileSampleFolders));\n                      Parallel.ForEach(options.RunFileSampleFolders, folder =>\n                      {\n                          foreach (var file in Directory.GetFiles(Path.GetFullPath(folder), \"*.cs\"))\n                          {\n                              CommandExecutor.ExecuteCommandAndOutput($\"dotnet build \\\"{file}\\\"\")\n                                  .EnsureSuccessExitCode();\n                          }\n                      });\n                  });\n            })\n            .WithTask(\"test\", b =>\n            {\n                b.WithDescription(\"dotnet test\")\n                    .WithDependency(\"build\")\n                    .WithExecution(() =>\n                    {\n                        var disableGitHubReport = EnvHelper.BooleanVal(\"DISABLE_GITHUB_ACTIONS_TEST_LOGGER\");\n                        var enableGitHubReport =\n                            string.Equals(EnvHelper.Val(\"GITHUB_ACTIONS\"), \"true\", StringComparison.OrdinalIgnoreCase) &&\n                            !disableGitHubReport;\n                        var reportArguments = enableGitHubReport ? \" --report-github\" : string.Empty;\n\n                        foreach (var project in options.TestProjects ?? [])\n                        {\n                            CommandExecutor.ExecuteCommandAndOutput(\n                                $\"dotnet run --project {project}{reportArguments}\"\n                                ).EnsureSuccessExitCode();\n                        }\n                    })\n                    ;\n            })\n            .WithTask(\"pack\", b => b.WithDescription(\"dotnet pack\")\n                .WithDependency(\"test\")\n                .WithExecution(() =>\n                {\n                    if (options.SrcProjects is not { Length: > 0 })\n                        return;\n\n                    if (_stable)\n                    {\n                        foreach (var project in options.SrcProjects ?? [])\n                        {\n                            CommandExecutor.ExecuteCommandAndOutput(\n                                    $\"dotnet pack {project} -o {options.ArtifactsPath}\"\n                                ).EnsureSuccessExitCode();\n                        }\n                    }\n                    else\n                    {\n                        var suffix = $\"preview-{DateTime.UtcNow:yyyyMMdd-HHmmss}\";\n                        foreach (var project in options.SrcProjects ?? [])\n                        {\n                            CommandExecutor.ExecuteCommandAndOutput(\n                                $\"dotnet pack {project} -o {options.ArtifactsPath} --version-suffix {suffix}\"\n                                ).EnsureSuccessExitCode();\n                        }\n                    }\n\n                    if (_noPush)\n                    {\n                        ConsoleHelper.WriteLineWithColor(@\"Skip push there's noPush specified\", ConsoleColor.Yellow);\n                        return;\n                    }\n\n                    if (_branch == \"local\")\n                    {\n                        if (!options.AllowLocalPush)\n                        {\n                            Console.WriteLine(@\"Skip push since local branch is not allowed to push packages\");\n                            return;\n                        }\n                    }\n                    else\n                    {\n                        // check preview branch\n                        if (!_stable && _branch is not \"preview\")\n                        {\n                            Console.WriteLine($@\"Skip push since branch [{_branch}] not supported to push packages\");\n                            return;\n                        }\n                    }\n\n                    if (string.IsNullOrEmpty(_apiKey))\n                    {\n                        // try to get apiKey from environment variable\n                        _apiKey = options.FallbackNuGetApiKeyFunc();\n\n                        if (string.IsNullOrEmpty(_apiKey))\n                        {\n                            ConsoleHelper.WriteLineWithColor(@\"Skip push since there's no apiKey found\", ConsoleColor.Yellow);\n                            return;\n                        }\n                    }\n\n\n                    // push nuget packages\n                    var nugetSource = string.IsNullOrEmpty(_source) ? options.FallbackNuGetSourceFunc() : _source;\n                    nugetSource = string.IsNullOrEmpty(nugetSource) ? string.Empty : $\"--source {nugetSource}\";\n                    var pushArguments = $\" -k {_apiKey} --skip-duplicate {nugetSource}\";\n                    foreach (var file in Directory.GetFiles(options.ArtifactsPath, \"*.nupkg\"))\n                    {\n                        var commandText = $\"dotnet nuget push {file} {pushArguments}\";\n                        CommandExecutor.ExecuteCommandAndOutput(commandText).EnsureSuccessExitCode();\n                    }\n                }))\n            .WithTask(\"Default\", b => b.WithDependency(\"pack\"))\n            ;\n#if NET\n        foreach (var (task, configure) in options.TaskOverride)\n        {\n#else\n        foreach (var pair in options.TaskOverride)\n        {\n            var task = pair.Key;\n            var configure = pair.Value;\n#endif\n            builder.WithTask(task, configure);\n        }\n\n        _buildProcess = ((BuildProcessBuilder)builder).Build();\n    }\n\n    public async Task ExecuteAsync(string[] args, CancellationToken cancellation = default)\n    {\n        _apiKey = CommandLineParser.Val(args, \"apiKey\");\n        _source = CommandLineParser.Val(args, \"source\");\n        _stable = CommandLineParser.BooleanVal(args, \"stable\");\n        _noPush = CommandLineParser.BooleanVal(args, \"noPush\");\n        if (string.IsNullOrEmpty(_branch))\n        {\n            _branch = CommandLineParser.Val(args, \"branch\", \"local\");\n        }\n        Debug.Assert(_branch is not null);\n        if (!_stable)\n        {\n            _stable = _branch is \"main\" or \"master\" ||\n                      _branch?.StartsWith(\"release/\", StringComparison.OrdinalIgnoreCase) == true;\n        }\n        else\n        {\n            // only specify `stable` locally\n            _stable = _branch == \"local\";\n        }\n        var target = CommandLineParser.Val(args, \"target\", \"Default\");\n        await _buildProcess.ExecuteAsync(target, cancellation);\n    }\n\n    public static DotNetPackageBuildProcess Create(Action<DotNetBuildProcessOptions> configure)\n    {\n        var options = new DotNetBuildProcessOptions();\n        configure(options);\n        return new DotNetPackageBuildProcess(options);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Combinatorics/Combinations.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Collections;\nusing System.Numerics;\n\nnamespace WeihanLi.Common.Helpers.Combinatorics;\n\n/// <summary>\n/// Combinations defines a sequence of all possible subsets of a particular size from the set of values.\n/// Within the returned set, there is no prescribed order.\n/// This follows the mathematical concept of choose.\n/// For example, put <c>10</c> dominoes in a hat and pick <c>5</c>.\n/// The number of possible combinations is defined as \"10 choose 5\", which is calculated as <c>(10!) / ((10 - 5)! * 5!)</c>.\n/// </summary>\n/// <remarks>\n/// The MetaCollectionType parameter of the constructor allows for the creation of\n/// two types of sets,  those with and without repetition in the output set when\n/// presented with repetition in the input set.\n///\n/// When given a input collect {A B C} and lower index of 2, the following sets are generated:\n/// MetaCollectionType.WithRepetition =>\n/// {A A}, {A B}, {A C}, {B B}, {B C}, {C C}\n/// MetaCollectionType.WithoutRepetition =>\n/// {A B}, {A C}, {B C}\n///\n/// Input sets with multiple equal values will generate redundant combinations in proportion\n/// to the likelihood of outcome.  For example, {A A B B} and a lower index of 3 will generate:\n/// {A A B} {A A B} {A B B} {A B B}\n/// </remarks>\n/// <typeparam name=\"T\">The type of the values within the list.</typeparam>\npublic sealed class Combinations<T> : IEnumerable<IReadOnlyList<T>>\n{\n    /// <summary>\n    /// Create a combination set from the provided list of values.\n    /// The upper index is calculated as values.Count, the lower index is specified.\n    /// Collection type defaults to MetaCollectionType.WithoutRepetition\n    /// </summary>\n    /// <param name=\"values\">List of values to select combinations from.</param>\n    /// <param name=\"lowerIndex\">The size of each combination set to return.</param>\n    public Combinations(IEnumerable<T> values, int lowerIndex)\n        : this(values, lowerIndex, GenerateOption.WithoutRepetition)\n    {\n    }\n\n    /// <summary>\n    /// Create a combination set from the provided list of values.\n    /// The upper index is calculated as values.Count, the lower index is specified.\n    /// </summary>\n    /// <param name=\"values\">List of values to select combinations from.</param>\n    /// <param name=\"lowerIndex\">The size of each combination set to return.</param>\n    /// <param name=\"type\">The type of Combinations set to generate.</param>\n    public Combinations(IEnumerable<T> values, int lowerIndex, GenerateOption type)\n    {\n        // Copy the array and parameters and then create a map of booleans that will\n        // be used by a permutations object to reference the subset.  This map is slightly\n        // different based on whether the type is with or without repetition.\n        //\n        // When the type is WithoutRepetition, then a map of upper index elements is\n        // created with lower index false's.\n        // E.g. 8 choose 3 generates:\n        // Map: {1 1 1 1 1 0 0 0}\n        // Note: For sorting reasons, false denotes inclusion in output.\n        //\n        // When the type is WithRepetition, then a map of upper index - 1 + lower index\n        // elements is created with the falses indicating that the 'current' element should\n        // be included and the trues meaning to advance the 'current' element by one.\n        // E.g. 8 choose 3 generates:\n        // Map: {1 1 1 1 1 1 1 1 0 0 0} (7 trues, 3 falses).\n\n        Type = type;\n        LowerIndex = lowerIndex;\n        _myValues = values is IList<T> list ? list : Guard.NotNull(values).ToArray();\n        List<bool> myMap;\n        if (type == GenerateOption.WithoutRepetition)\n        {\n            myMap = new List<bool>(_myValues.Count);\n            myMap.AddRange(_myValues.Select((_, i) => i < _myValues.Count - LowerIndex));\n        }\n        else\n        {\n            myMap = new List<bool>(_myValues.Count + LowerIndex - 1);\n            for (var i = 0; i < _myValues.Count - 1; ++i)\n                myMap.Add(true);\n            for (var i = 0; i < LowerIndex; ++i)\n                myMap.Add(false);\n        }\n\n        _myPermutations = new Permutations<bool>(myMap);\n    }\n\n    /// <summary>\n    /// Gets an enumerator for collecting the list of combinations.\n    /// </summary>\n    /// <returns>The enumerator.</returns>\n    public IEnumerator<IReadOnlyList<T>> GetEnumerator() => new Enumerator(this);\n\n    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n\n    /// <summary>\n    /// The enumerator that enumerates each meta-collection of the enclosing Combinations class.\n    /// </summary>\n    private sealed class Enumerator : IEnumerator<IReadOnlyList<T>>\n    {\n        /// <summary>\n        /// Construct a enumerator with the parent object.\n        /// </summary>\n        /// <param name=\"source\">The source combinations object.</param>\n        public Enumerator(Combinations<T> source)\n        {\n            _myParent = source;\n            _myPermutationsEnumerator = (Permutations<bool>.Enumerator)_myParent._myPermutations.GetEnumerator();\n        }\n\n        void IEnumerator.Reset() => throw new NotSupportedException();\n\n        /// <summary>\n        /// Advances to the next combination of items from the set.\n        /// </summary>\n        /// <returns>True if successfully moved to next combination, False if no more unique combinations exist.</returns>\n        /// <remarks>\n        /// The heavy lifting is done by the permutations object, the combination is generated\n        /// by creating a new list of those items that have a true in the permutation parallel array.\n        /// </remarks>\n        public bool MoveNext()\n        {\n            var ret = _myPermutationsEnumerator.MoveNext();\n            _myCurrentList = null;\n            return ret;\n        }\n\n        /// <summary>\n        /// The current combination\n        /// </summary>\n        public IReadOnlyList<T> Current\n        {\n            get\n            {\n                ComputeCurrent();\n                return _myCurrentList!;\n            }\n        }\n\n        object IEnumerator.Current => Current;\n\n        /// <inheritdoc />\n        public void Dispose() => _myPermutationsEnumerator.Dispose();\n\n        /// <summary>\n        /// The only complex function of this entire wrapper, ComputeCurrent() creates\n        /// a list of original values from the bool permutation provided.\n        /// The exception for accessing current (InvalidOperationException) is generated\n        /// by the call to .Current on the underlying enumeration.\n        /// </summary>\n        /// <remarks>\n        /// To compute the current list of values, the underlying permutation object\n        /// which moves with this enumerator, is scanned differently based on the type.\n        /// The items have only two values, true and false, which have different meanings:\n        ///\n        /// For type WithoutRepetition, the output is a straightforward subset of the input array.\n        /// E.g. 6 choose 3 without repetition\n        /// Input array:   {A B C D E F}\n        /// Permutations:  {0 1 0 0 1 1}\n        /// Generates set: {A   C D    }\n        /// Note: size of permutation is equal to upper index.\n        ///\n        /// For type WithRepetition, the output is defined by runs of characters and when to\n        /// move to the next element.\n        /// E.g. 6 choose 5 with repetition\n        /// Input array:   {A B C D E F}\n        /// Permutations:  {0 1 0 0 1 1 0 0 1 1}\n        /// Generates set: {A   B B     D D    }\n        /// Note: size of permutation is equal to upper index - 1 + lower index.\n        /// </remarks>\n        private void ComputeCurrent()\n        {\n            if (_myCurrentList != null)\n                return;\n\n            _myCurrentList = new List<T>(_myParent.LowerIndex);\n            var index = 0;\n            var currentPermutation = _myPermutationsEnumerator.Current;\n            foreach (var p in currentPermutation)\n            {\n                if (!p)\n                {\n                    _myCurrentList.Add(_myParent._myValues[index]);\n                    if (_myParent.Type == GenerateOption.WithoutRepetition)\n                        ++index;\n                }\n                else\n                {\n                    ++index;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Parent object this is an enumerator for.\n        /// </summary>\n        private readonly Combinations<T> _myParent;\n\n        /// <summary>\n        /// The current list of values, this is lazy evaluated by the Current property.\n        /// </summary>\n        private List<T>? _myCurrentList;\n\n        /// <summary>\n        /// An enumerator of the parents list of lexicographic orderings.\n        /// </summary>\n        private readonly Permutations<bool>.Enumerator _myPermutationsEnumerator;\n    }\n\n    /// <summary>\n    /// The number of unique combinations that are defined in this meta-collection.\n    /// This value is mathematically defined as Choose(M, N) where M is the set size\n    /// and N is the subset size.  This is M! / (N! * (M-N)!).\n    /// </summary>\n    public BigInteger Count => _myPermutations.Count;\n\n    /// <summary>\n    /// The type of Combinations set that is generated.\n    /// </summary>\n    public GenerateOption Type { get; }\n\n    /// <summary>\n    /// The upper index of the meta-collection, equal to the number of items in the initial set.\n    /// </summary>\n    public int UpperIndex => _myValues.Count;\n\n    /// <summary>\n    /// The lower index of the meta-collection, equal to the number of items returned each iteration.\n    /// </summary>\n    public int LowerIndex { get; }\n\n    /// <summary>\n    /// Copy of values object is initialized with, required for enumerator reset.\n    /// </summary>\n    private readonly IList<T> _myValues;\n\n    /// <summary>\n    /// Permutations object that handles permutations on booleans for combination inclusion.\n    /// </summary>\n    private readonly Permutations<bool> _myPermutations;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Combinatorics/GenerateOption.cs",
    "content": "﻿namespace WeihanLi.Common.Helpers.Combinatorics;\n\n/// <summary>\n/// Indicates whether a permutation, combination or variation generates equivalent result sets.\n/// </summary>\npublic enum GenerateOption\n{\n    /// <summary>\n    /// Do not generate equivalent result sets.\n    /// </summary>\n    WithoutRepetition,\n\n    /// <summary>\n    /// Generate equivalent result sets.\n    /// </summary>\n    WithRepetition,\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Combinatorics/Permutations.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Collections;\nusing System.Numerics;\n\nnamespace WeihanLi.Common.Helpers.Combinatorics;\n\n/// <summary>\n/// Permutations defines a sequence of all possible orderings of a set of values.\n/// </summary>\n/// <remarks>\n/// When given a input collect {A A B}, the following sets are generated:\n/// MetaCollectionType.WithRepetition =>\n/// {A A B}, {A B A}, {A A B}, {A B A}, {B A A}, {B A A}\n/// MetaCollectionType.WithoutRepetition =>\n/// {A A B}, {A B A}, {B A A}\n///\n/// When generating non-repetition sets, ordering is based on the lexicographic\n/// ordering of the lists based on the provided Comparer.\n/// If no comparer is provided, then T must be IComparable on T.\n///\n/// When generating repetition sets, no comparisons are performed and therefore\n/// no comparer is required and T does not need to be IComparable.\n/// </remarks>\n/// <typeparam name=\"T\">The type of the values within the list.</typeparam>\npublic sealed class Permutations<T> : IEnumerable<IReadOnlyList<T>>\n{\n    /// <summary>\n    /// Create a permutation set from the provided list of values.\n    /// The values (T) must implement IComparable.\n    /// If T does not implement IComparable use a constructor with an explicit IComparer.\n    /// The repetition type defaults to MetaCollectionType.WithholdRepetitionSets\n    /// </summary>\n    /// <param name=\"values\">List of values to permute.</param>\n    public Permutations(IEnumerable<T> values)\n        : this(values, GenerateOption.WithoutRepetition, null)\n    {\n    }\n\n    /// <summary>\n    /// Create a permutation set from the provided list of values.\n    /// If type is MetaCollectionType.WithholdRepetitionSets, then values (T) must implement IComparable.\n    /// If T does not implement IComparable use a constructor with an explicit IComparer.\n    /// </summary>\n    /// <param name=\"values\">List of values to permute.</param>\n    /// <param name=\"type\">The type of permutation set to calculate.</param>\n    public Permutations(IEnumerable<T> values, GenerateOption type)\n        : this(values, type, null)\n    {\n    }\n\n    /// <summary>\n    /// Create a permutation set from the provided list of values.\n    /// The values will be compared using the supplied IComparer.\n    /// The repetition type defaults to MetaCollectionType.WithholdRepetitionSets\n    /// </summary>\n    /// <param name=\"values\">List of values to permute.</param>\n    /// <param name=\"comparer\">Comparer used for defining the lexicographic order.</param>\n    public Permutations(IEnumerable<T> values, IComparer<T>? comparer)\n        : this(values, GenerateOption.WithoutRepetition, comparer)\n    {\n    }\n\n    /// <summary>\n    /// Create a permutation set from the provided list of values.\n    /// If type is MetaCollectionType.WithholdRepetitionSets, then the values will be compared using the supplied IComparer.\n    /// </summary>\n    /// <param name=\"values\">List of values to permute.</param>\n    /// <param name=\"type\">The type of permutation set to calculate.</param>\n    /// <param name=\"comparer\">Comparer used for defining the lexicographic order.</param>\n    public Permutations(IEnumerable<T> values, GenerateOption type, IComparer<T>? comparer)\n    {\n        Guard.NotNull(values);\n\n        // Copy information provided and then create a parallel int array of lexicographic\n        // orders that will be used for the actual permutation algorithm.\n        // The input array is first sorted as required for WithoutRepetition and always just for consistency.\n        // This array is constructed one of two way depending on the type of the collection.\n        //\n        // When type is MetaCollectionType.WithRepetition, then all N! permutations are returned\n        // and the lexicographic orders are simply generated as 1, 2, ... N.\n        // E.g.\n        // Input array:          {A A B C D E E}\n        // Lexicographic Orders: {1 2 3 4 5 6 7}\n        //\n        // When type is MetaCollectionType.WithoutRepetition, then fewer are generated, with each\n        // identical element in the input array not repeated.  The lexicographic sort algorithm\n        // handles this natively as long as the repetition is repeated.\n        // E.g.\n        // Input array:          {A A B C D E E}\n        // Lexicographic Orders: {1 1 2 3 4 5 5}\n\n        Type = type;\n        _myValues = values as T[] ?? values.ToArray();\n        _myLexicographicOrders = new int[_myValues.Length];\n\n        if (type == GenerateOption.WithRepetition)\n        {\n            for (var i = 0; i < _myLexicographicOrders.Length; ++i)\n            {\n                _myLexicographicOrders[i] = i;\n            }\n        }\n        else\n        {\n            comparer ??= Comparer<T>.Default;\n\n            Array.Sort(_myValues, comparer);\n\n            var j = 1;\n            if (_myLexicographicOrders.Length > 0)\n            {\n                _myLexicographicOrders[0] = j;\n            }\n\n            for (var i = 1; i < _myLexicographicOrders.Length; ++i)\n            {\n                if (comparer.Compare(_myValues[i - 1], _myValues[i]) != 0)\n                {\n                    ++j;\n                }\n\n                _myLexicographicOrders[i] = j;\n            }\n        }\n\n        Count = GetCount();\n    }\n\n    /// <summary>\n    /// Gets an enumerator for collecting the list of permutations.\n    /// </summary>\n    /// <returns>The enumerator.</returns>\n    public IEnumerator<IReadOnlyList<T>> GetEnumerator() => new Enumerator(this);\n\n    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n\n    /// <summary>\n    /// The enumerator that enumerates each meta-collection of the enclosing Permutations class.\n    /// </summary>\n    internal sealed class Enumerator : IEnumerator<IReadOnlyList<T>>\n    {\n        /// <summary>\n        /// Construct a enumerator with the parent object.\n        /// </summary>\n        /// <param name=\"source\">The source Permutations object.</param>\n        public Enumerator(Permutations<T> source)\n        {\n            _myParent = Guard.NotNull(source);\n            _myLexicographicalOrders = new int[source._myLexicographicOrders.Length];\n            _myValues = new List<T>(source._myValues.Length);\n            source._myLexicographicOrders.CopyTo(_myLexicographicalOrders, 0);\n            _myPosition = Position.BeforeFirst;\n        }\n\n        void IEnumerator.Reset() => throw new NotSupportedException();\n\n        /// <summary>\n        /// Advances to the next permutation.\n        /// </summary>\n        /// <returns>True if successfully moved to next permutation, False if no more permutations exist.</returns>\n        /// <remarks>\n        /// Continuation was tried (i.e. yield return) by was not nearly as efficient.\n        /// Performance is further increased by using value types and removing generics, that is, the LexicographicOrder parellel array.\n        /// This is a issue with the .NET CLR not optimizing as well as it could in this infrequently used scenario.\n        /// </remarks>\n        public bool MoveNext()\n        {\n            switch (_myPosition)\n            {\n                case Position.BeforeFirst:\n                    _myValues.AddRange(_myParent._myValues);\n                    _myPosition = Position.InSet;\n                    break;\n\n                case Position.InSet:\n                    if (_myValues.Count < 2)\n                    {\n                        _myPosition = Position.AfterLast;\n                    }\n                    else if (!NextPermutation())\n                    {\n                        _myPosition = Position.AfterLast;\n                    }\n                    break;\n\n                case Position.AfterLast:\n                    break;\n\n                default:\n                    throw new ArgumentOutOfRangeException();\n            }\n            return _myPosition != Position.AfterLast;\n        }\n\n        object IEnumerator.Current => Current;\n\n        /// <summary>\n        /// The current permutation.\n        /// </summary>\n        public IReadOnlyList<T> Current\n        {\n            get\n            {\n                if (_myPosition == Position.InSet)\n                    return new List<T>(_myValues);\n\n                throw new InvalidOperationException();\n            }\n        }\n\n        /// <inheritdoc />\n        public void Dispose()\n        {\n        }\n\n        /// <summary>\n        /// Calculates the next lexicographical permutation of the set.\n        /// This is a permutation with repetition where values that compare as equal will not\n        /// swap positions to create a new permutation.\n        /// http://www.cut-the-knot.org/do_you_know/AllPerm.shtml\n        /// E. W. Dijkstra, A Discipline of Programming, Prentice-Hall, 1997\n        /// </summary>\n        /// <returns>True if a new permutation has been returned, false if not.</returns>\n        /// <remarks>\n        /// This uses the integers of the lexicographical order of the values so that any\n        /// comparison of values are only performed during initialization.\n        /// </remarks>\n        private bool NextPermutation()\n        {\n            var i = _myLexicographicalOrders.Length - 1;\n\n            while (_myLexicographicalOrders[i - 1] >= _myLexicographicalOrders[i])\n            {\n                --i;\n                if (i == 0)\n                {\n                    return false;\n                }\n            }\n\n            var j = _myLexicographicalOrders.Length;\n\n            while (_myLexicographicalOrders[j - 1] <= _myLexicographicalOrders[i - 1])\n            {\n                --j;\n            }\n\n            Swap(i - 1, j - 1);\n\n            ++i;\n\n            j = _myLexicographicalOrders.Length;\n\n            while (i < j)\n            {\n                Swap(i - 1, j - 1);\n                ++i;\n                --j;\n            }\n            return true;\n        }\n\n        /// <summary>\n        /// Helper function for swapping two elements within the internal collection.\n        /// This swaps both the lexicographical order and the values, maintaining the parallel array.\n        /// </summary>\n        private void Swap(int i, int j)\n        {\n            (_myValues[j], _myValues[i]) = (_myValues[i], _myValues[j]);\n            _myKviTemp = _myLexicographicalOrders[i];\n            _myLexicographicalOrders[i] = _myLexicographicalOrders[j];\n            _myLexicographicalOrders[j] = _myKviTemp;\n        }\n\n        /// <summary>\n        /// Single instance of swap variable for int, small performance improvement over declaring in Swap function scope.\n        /// </summary>\n        private int _myKviTemp;\n\n        /// <summary>\n        /// Flag indicating the position of the enumerator.\n        /// </summary>\n        private Position _myPosition;\n\n        /// <summary>\n        /// Parallel array of integers that represent the location of items in the myValues array.\n        /// This is generated at Initialization and is used as a performance speed up rather that\n        /// comparing T each time, much faster to let the CLR optimize around integers.\n        /// </summary>\n        private readonly int[] _myLexicographicalOrders;\n\n        /// <summary>\n        /// The list of values that are current to the enumerator.\n        /// </summary>\n        private readonly List<T> _myValues;\n\n        /// <summary>\n        /// The set of permutations that this enumerator enumerates.\n        /// </summary>\n        private readonly Permutations<T> _myParent;\n\n        /// <summary>\n        /// Internal position type for tracking enumerator position.\n        /// </summary>\n        private enum Position\n        {\n            BeforeFirst,\n            InSet,\n            AfterLast\n        }\n    }\n\n    /// <summary>\n    /// The count of all permutations that will be returned.\n    /// If <see cref=\"Type\"/> is <see cref=\"GenerateOption.WithoutRepetition\"/>, then this does not count equivalent result sets.\n    /// I.e., count of permutations of \"AAB\" will be 3 instead of 6.\n    /// If <see cref=\"Type\"/> is <see cref=\"GenerateOption.WithRepetition\"/>, then this is all combinations and is therefore N!, where N is the number of values in the input set.\n    /// </summary>\n    public BigInteger Count { get; }\n\n    /// <summary>\n    /// The type of permutations set that is generated.\n    /// </summary>\n    public GenerateOption Type { get; }\n\n    /// <summary>\n    /// The upper index of the meta-collection, equal to the number of items in the input set.\n    /// </summary>\n    public int UpperIndex => _myValues.Length;\n\n    /// <summary>\n    /// The lower index of the meta-collection, equal to the number of items returned each iteration.\n    /// This is always equal to <see cref=\"UpperIndex\"/>.\n    /// </summary>\n    public int LowerIndex => _myValues.Length;\n\n    /// <summary>\n    /// Calculates the total number of permutations that will be returned.\n    /// As this can grow very large, extra effort is taken to avoid overflowing the accumulator.\n    /// While the algorithm looks complex, it really is just collecting numerator and denominator terms\n    /// and cancelling out all of the denominator terms before taking the product of the numerator terms.\n    /// </summary>\n    /// <returns>The number of permutations.</returns>\n    private BigInteger GetCount()\n    {\n        var runCount = 1;\n        var divisors = Enumerable.Empty<int>();\n        var numerators = Enumerable.Empty<int>();\n\n        for (var i = 1; i < _myLexicographicOrders.Length; ++i)\n        {\n            numerators = numerators.Concat(SmallPrimeUtility.Factor(i + 1));\n\n            if (_myLexicographicOrders[i] == _myLexicographicOrders[i - 1])\n            {\n                ++runCount;\n            }\n            else\n            {\n                for (var f = 2; f <= runCount; ++f)\n                    divisors = divisors.Concat(SmallPrimeUtility.Factor(f));\n\n                runCount = 1;\n            }\n        }\n\n        for (var f = 2; f <= runCount; ++f)\n            divisors = divisors.Concat(SmallPrimeUtility.Factor(f));\n\n        return SmallPrimeUtility.EvaluatePrimeFactors(\n            SmallPrimeUtility.DividePrimeFactors(numerators, divisors)\n        );\n    }\n\n    /// <summary>\n    /// A list of T that represents the order of elements as originally provided.\n    /// </summary>\n    private readonly T[] _myValues;\n\n    /// <summary>\n    /// Parallel array of integers that represent the location of items in the myValues array.\n    /// This is generated at Initialization and is used as a performance speed up rather that\n    /// comparing T each time, much faster to let the CLR optimize around integers.\n    /// </summary>\n    private readonly int[] _myLexicographicOrders;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Combinatorics/SmallPrimeUtility.cs",
    "content": "﻿using System.Collections;\nusing System.Numerics;\n\nnamespace WeihanLi.Common.Helpers.Combinatorics;\n\n/// <summary>\n/// Utility class that maintains a small table of prime numbers and provides\n/// simple implementations of Prime Factorization algorithms.\n/// This is a quick and dirty utility class to support calculations of permutation\n/// sets with indexes under 2^31.\n/// The prime table contains all primes up to Sqrt(2^31) which are all of the primes\n/// requires to factorize any Int32 positive integer.\n/// </summary>\ninternal static class SmallPrimeUtility\n{\n    /// <summary>\n    /// Performs a prime factorization of a given integer using the table of primes in PrimeTable.\n    /// Since this will only factor Int32 sized integers, a simple list of factors is returned instead\n    /// of the more scalable, but more difficult to consume, list of primes and associated exponents.\n    /// </summary>\n    /// <param name=\"i\">The number to factorize, must be positive.</param>\n    /// <returns>A simple list of factors.</returns>\n    public static List<int> Factor(int i)\n    {\n        var primeIndex = 0;\n        var prime = PrimeTable[primeIndex];\n        var factors = new List<int>();\n\n        while (i > 1)\n        {\n            var divResult = Math.DivRem(i, prime, out var remainder);\n\n            if (remainder == 0)\n            {\n                factors.Add(prime);\n                i = divResult;\n            }\n            else\n            {\n                ++primeIndex;\n                prime = PrimeTable[primeIndex];\n            }\n        }\n\n        return factors;\n    }\n\n    /// <summary>\n    /// Given two integers expressed as a list of prime factors, divides these numbers\n    /// and returns an integer also expressed as a set of prime factors.\n    /// If the result is not a integer, then the result is undefined.  That is, 11 / 5\n    /// when divided by this function will not yield a correct result.\n    /// As such, this function is ONLY useful for division with combinatorial results where\n    /// the result is known to be an integer AND the division occurs as the last operation(s).\n    /// </summary>\n    /// <param name=\"numerator\">Numerator argument, expressed as list of prime factors.</param>\n    /// <param name=\"denominator\">Denominator argument, expressed as list of prime factors.</param>\n    /// <returns>Resultant, expressed as list of prime factors.</returns>\n    public static List<int> DividePrimeFactors(IEnumerable<int> numerator, IEnumerable<int> denominator)\n    {\n        Guard.NotNull(denominator);\n        var product = Guard.NotNull(numerator).ToList();\n        foreach (var prime in denominator)\n            product.Remove(prime);\n        return product;\n    }\n\n    /// <summary>\n    /// Given a list of prime factors returns the long representation.\n    /// </summary>\n    /// <param name=\"value\">Integer, expressed as list of prime factors.</param>\n    /// <returns>Standard long representation.</returns>\n    public static BigInteger EvaluatePrimeFactors(IEnumerable<int> value)\n    {\n        Guard.NotNull(value);\n        BigInteger result = 1;\n        foreach (var prime in value)\n            result *= prime;\n        return result;\n    }\n\n    /// <summary>\n    /// Static initializer, set up prime table.\n    /// </summary>\n    static SmallPrimeUtility()\n    {\n        PrimeTable = CalculatePrimes();\n    }\n\n    /// <summary>\n    /// Calculate all primes up to Sqrt(2^32) = 2^16.\n    /// This table will be large enough for all factorizations for Int32's.\n    /// Small tables are best built using the Sieve Of Eratosthenes,\n    /// Reference: http://primes.utm.edu/glossary/page.php?sort=SieveOfEratosthenes\n    /// </summary>\n    private static IReadOnlyList<int> CalculatePrimes()\n    {\n        // Build Sieve Of Eratosthenes\n        var sieve = new BitArray(65536, true);\n        for (var possiblePrime = 2; possiblePrime <= 256; ++possiblePrime)\n        {\n            if (!sieve[possiblePrime]) continue;\n\n            // It is prime, so remove all future factors...\n            for (var nonPrime = 2 * possiblePrime; nonPrime < 65536; nonPrime += possiblePrime)\n                sieve[nonPrime] = false;\n        }\n\n        // Scan sieve for primes...\n        var primes = new List<int>();\n        for (var i = 2; i < 65536; ++i)\n        {\n            if (sieve[i])\n                primes.Add(i);\n        }\n\n        return primes;\n    }\n\n    /// <summary>\n    /// A List of all primes from 2 to 2^16.\n    /// </summary>\n    public static IReadOnlyList<int> PrimeTable { get; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Combinatorics/Variations.cs",
    "content": "﻿using System.Collections;\nusing System.Numerics;\n\nnamespace WeihanLi.Common.Helpers.Combinatorics;\n\n/// <summary>\n/// Variations defines a sequence of all possible ordered subsets of a particular size from the set of values.\n/// </summary>\n/// <remarks>\n/// The MetaCollectionType parameter of the constructor allows for the creation of\n/// normal Variations and Variations with Repetition.\n///\n/// When given an input collect {A B C} and lower index of 2, the following sets are generated:\n/// MetaCollectionType.WithoutRepetition generates 6 sets: =>\n///     {A B}, {A B}, {B A}, {B C}, {C A}, {C B}\n/// MetaCollectionType.WithRepetition generates 9 sets:\n///     {A A}, {A B}, {A B}, {B A}, {B B }, {B C}, {C A}, {C B}, {C C}\n///\n/// The equality of multiple inputs is not considered when generating variations.\n/// </remarks>\n/// <typeparam name=\"T\">The type of the values within the list.</typeparam>\npublic sealed class Variations<T> : IEnumerable<IReadOnlyList<T>>\n{\n    /// <summary>\n    /// Create a variation set from the indicated list of values.\n    /// The upper index is calculated as values.Count, the lower index is specified.\n    /// Collection type defaults to MetaCollectionType.WithoutRepetition\n    /// </summary>\n    /// <param name=\"values\">List of values to select Variations from.</param>\n    /// <param name=\"lowerIndex\">The size of each variation set to return.</param>\n    public Variations(IEnumerable<T> values, int lowerIndex)\n        : this(values, lowerIndex, GenerateOption.WithoutRepetition)\n    {\n    }\n\n    /// <summary>\n    /// Create a variation set from the indicated list of values.\n    /// The upper index is calculated as values.Count, the lower index is specified.\n    /// </summary>\n    /// <param name=\"values\">List of values to select variations from.</param>\n    /// <param name=\"lowerIndex\">The size of each variation set to return.</param>\n    /// <param name=\"type\">Type indicates whether to use repetition in set generation.</param>\n    public Variations(IEnumerable<T> values, int lowerIndex, GenerateOption type)\n    {\n        Type = type;\n        LowerIndex = lowerIndex;\n        _myValues = values is List<T> list ? list : values.ToList();\n\n        if (type != GenerateOption.WithoutRepetition)\n        {\n            return;\n        }\n\n        var myMap = new List<int>(_myValues.Count);\n        var index = 0;\n        for (var i = 0; i < _myValues.Count; ++i)\n            myMap.Add(i >= _myValues.Count - LowerIndex ? index++ : int.MaxValue);\n\n        _myPermutations = new Permutations<int>(myMap);\n    }\n\n    /// <summary>\n    /// Gets an enumerator for the collection of Variations.\n    /// </summary>\n    /// <returns>The enumerator.</returns>\n    public IEnumerator<IReadOnlyList<T>> GetEnumerator() =>\n        Type == GenerateOption.WithRepetition ?\n            (IEnumerator<IReadOnlyList<T>>)new EnumeratorWithRepetition(this) :\n            new EnumeratorWithoutRepetition(this);\n\n    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n\n    /// <summary>\n    /// An enumerator for Variations when the type is set to WithRepetition.\n    /// </summary>\n    /// <remarks>\n    /// Construct a enumerator with the parent object.\n    /// </remarks>\n    /// <param name=\"source\">The source Variations object.</param>\n    private sealed class EnumeratorWithRepetition(Variations<T> source) : IEnumerator<IReadOnlyList<T>>\n    {\n        void IEnumerator.Reset() => throw new NotSupportedException();\n\n        /// <summary>\n        /// Advances to the next variation.\n        /// </summary>\n        /// <returns>True if successfully moved to next variation, False if no more variations exist.</returns>\n        /// <remarks>\n        /// Increments the internal myListIndexes collection by incrementing the last index\n        /// and overflow/carrying into others just like grade-school arithmetic.  If the\n        /// final carry flag is set, then we would wrap around and are therefore done.\n        /// </remarks>\n        public bool MoveNext()\n        {\n            var carry = 1;\n            if (_myListIndexes == null)\n            {\n                _myListIndexes = new List<int>(_myParent.LowerIndex);\n                for (var i = 0; i < _myParent.LowerIndex; ++i)\n                {\n                    _myListIndexes.Add(0);\n                }\n                carry = 0;\n            }\n            else\n            {\n                for (var i = _myListIndexes.Count - 1; i >= 0 && carry > 0; --i)\n                {\n                    _myListIndexes[i] += carry;\n                    carry = 0;\n\n                    if (_myListIndexes[i] < _myParent.UpperIndex)\n                    {\n                        continue;\n                    }\n\n                    _myListIndexes[i] = 0;\n                    carry = 1;\n                }\n            }\n            _myCurrentList = null;\n            return carry != 1;\n        }\n\n        /// <summary>\n        /// The current variation\n        /// </summary>\n        public IReadOnlyList<T> Current\n        {\n            get\n            {\n                ComputeCurrent();\n                return _myCurrentList!;\n            }\n        }\n\n        object IEnumerator.Current => Current;\n\n        /// <inheritdoc />\n        public void Dispose()\n        {\n        }\n\n        /// <summary>\n        /// Computes the current list based on the internal list index.\n        /// </summary>\n        private void ComputeCurrent()\n        {\n            if (_myCurrentList != null)\n            {\n                return;\n            }\n\n            _ = _myListIndexes ?? throw new InvalidOperationException($\"Cannot call {nameof(Current)} before calling {nameof(MoveNext)}.\");\n\n            _myCurrentList = new List<T>(_myListIndexes.Count);\n            foreach (var index in _myListIndexes)\n                _myCurrentList.Add(_myParent._myValues[index]);\n        }\n\n        /// <summary>\n        /// Parent object this is an enumerator for.\n        /// </summary>\n        private readonly Variations<T> _myParent = source;\n\n        /// <summary>\n        /// The current list of values, this is lazy evaluated by the Current property.\n        /// </summary>\n        private List<T>? _myCurrentList = null;\n\n        /// <summary>\n        /// An enumerator of the parents list of lexicographic orderings.\n        /// </summary>\n        private List<int>? _myListIndexes = null;\n    }\n\n    /// <summary>\n    /// An enumerator for Variations when the type is set to WithoutRepetition.\n    /// </summary>\n    private sealed class EnumeratorWithoutRepetition : IEnumerator<IReadOnlyList<T>>\n    {\n        /// <summary>\n        /// Construct a enumerator with the parent object.\n        /// </summary>\n        /// <param name=\"source\">The source Variations object.</param>\n        public EnumeratorWithoutRepetition(Variations<T> source)\n        {\n            _myParent = source;\n            _myPermutationsEnumerator = (Permutations<int>.Enumerator)_myParent._myPermutations!.GetEnumerator();\n        }\n\n        void IEnumerator.Reset() => throw new NotSupportedException();\n\n        /// <summary>\n        /// Advances to the next variation.\n        /// </summary>\n        /// <returns>True if successfully moved to next variation, False if no more variations exist.</returns>\n        public bool MoveNext()\n        {\n            var ret = _myPermutationsEnumerator.MoveNext();\n            _myCurrentList = null;\n            return ret;\n        }\n\n        /// <summary>\n        /// The current variation.\n        /// </summary>\n        public IReadOnlyList<T> Current\n        {\n            get\n            {\n                ComputeCurrent();\n                return _myCurrentList!;\n            }\n        }\n\n        object IEnumerator.Current => Current;\n\n        /// <inheritdoc />\n        public void Dispose() => _myPermutationsEnumerator.Dispose();\n\n        /// <summary>\n        /// Creates a list of original values from the int permutation provided.\n        /// The exception for accessing current (InvalidOperationException) is generated\n        /// by the call to .Current on the underlying enumeration.\n        /// </summary>\n        /// <remarks>\n        /// To compute the current list of values, the element to use is determined by\n        /// a permutation position with a non-MaxValue value.  It is placed at the position in the\n        /// output that the index value indicates.\n        ///\n        /// E.g. Variations of 6 choose 3 without repetition\n        /// Input array:   {A B C D E F}\n        /// Permutations:  {- 1 - - 3 2} (- is Int32.MaxValue)\n        /// Generates set: {B F E}\n        /// </remarks>\n        private void ComputeCurrent()\n        {\n            if (_myCurrentList != null)\n            {\n                return;\n            }\n\n            _myCurrentList = new List<T>(_myParent.LowerIndex);\n            var index = 0;\n            var currentPermutation = _myPermutationsEnumerator.Current;\n\n            for (var i = 0; i < _myParent.LowerIndex; ++i)\n            {\n                _myCurrentList.Add(_myParent._myValues[0]);\n            }\n\n            foreach (var position in currentPermutation)\n            {\n                if (position != int.MaxValue)\n                {\n                    _myCurrentList[position] = _myParent._myValues[index];\n                    if (_myParent.Type == GenerateOption.WithoutRepetition)\n                    {\n                        ++index;\n                    }\n                }\n                else\n                {\n                    ++index;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Parent object this is an enumerator for.\n        /// </summary>\n        private readonly Variations<T> _myParent;\n\n        /// <summary>\n        /// The current list of values, this is lazy evaluated by the Current property.\n        /// </summary>\n        private List<T>? _myCurrentList;\n\n        /// <summary>\n        /// An enumerator of the parents list of lexicographic orderings.\n        /// </summary>\n        private readonly Permutations<int>.Enumerator _myPermutationsEnumerator;\n    }\n\n    /// <summary>\n    /// The number of unique variations that are defined in this meta-collection.\n    /// </summary>\n    /// <remarks>\n    /// Variations with repetitions does not behave like other meta-collections and it's\n    /// count is equal to N^P, where N is the upper index and P is the lower index.\n    /// </remarks>\n    public BigInteger Count => Type == GenerateOption.WithoutRepetition ? _myPermutations!.Count : BigInteger.Pow(UpperIndex, LowerIndex);\n\n    /// <summary>\n    /// The type of Variations set that is generated.\n    /// </summary>\n    public GenerateOption Type { get; }\n\n    /// <summary>\n    /// The upper index of the meta-collection, equal to the number of items in the initial set.\n    /// </summary>\n    public int UpperIndex => _myValues.Count;\n\n    /// <summary>\n    /// The lower index of the meta-collection, equal to the number of items returned each iteration.\n    /// </summary>\n    public int LowerIndex { get; }\n\n    /// <summary>\n    /// Copy of values object is initialized with, required for enumerator reset.\n    /// </summary>\n    private readonly List<T> _myValues;\n\n    /// <summary>\n    /// Permutations object that handles permutations on int for variation inclusion and ordering.\n    /// </summary>\n    private readonly Permutations<int>? _myPermutations;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/CommandExecutor.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Diagnostics;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class CommandExecutor\n{\n    private static readonly char[] SpaceSeparator = [' '];\n\n    /// <summary>\n    /// Execute command\n    /// </summary>\n    /// <param name=\"command\">command with arguments</param>\n    /// <param name=\"workingDirectory\">working directory for the command</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <returns>exit code</returns>\n    public static int ExecuteCommand(string command, string? workingDirectory = null, Action<ProcessStartInfo>? configure = null)\n    {\n        Guard.NotNullOrEmpty(command);\n        var cmd = command.Split(SpaceSeparator, 2);\n        return Execute(cmd[0], cmd.Length > 1 ? cmd[1] : null, workingDirectory, configure);\n    }\n\n    /// <summary>\n    /// Execute command and capture output\n    /// </summary>\n    /// <param name=\"command\">command with arguments</param>\n    /// <param name=\"workingDirectory\">working directory for the command</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <returns>command execute result</returns>\n    public static CommandResult ExecuteCommandAndCapture(string command, string? workingDirectory = null, Action<ProcessStartInfo>? configure = null)\n    {\n        Guard.NotNullOrEmpty(command);\n        var cmd = command.Split(SpaceSeparator, 2);\n        return ExecuteAndCapture(cmd[0], cmd.Length > 1 ? cmd[1] : null, workingDirectory, configure);\n    }\n\n    /// <summary>\n    /// Execute command with a process\n    /// </summary>\n    /// <param name=\"command\">executable command and argument</param>\n    /// <param name=\"stdout\">stdout writer, write to console by default</param>\n    /// <param name=\"stderr\">stderr writer, write to console by default</param>\n    /// <param name=\"workingDirectory\">working directory</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <returns>exit code</returns>\n    public static int ExecuteCommandAndOutput(string command,\n        TextWriter? stdout = null, TextWriter? stderr = null,\n        string? workingDirectory = null, Action<ProcessStartInfo>? configure = null)\n    {\n        Guard.NotNullOrEmpty(command);\n        var cmd = command.Split(SpaceSeparator, 2);\n        return ExecuteAndOutput(cmd[0], cmd.Length > 1 ? cmd[1] : null, stdout, stderr, workingDirectory, configure);\n    }\n\n    /// <summary>\n    /// Execute command async\n    /// </summary>\n    /// <param name=\"command\">command with arguments</param>\n    /// <param name=\"workingDirectory\">working directory for the command</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>exit code</returns>\n    public static Task<int> ExecuteCommandAsync(string command, string? workingDirectory = null, Action<ProcessStartInfo>? configure = null, CancellationToken cancellationToken = default)\n    {\n        Guard.NotNullOrEmpty(command);\n        var cmd = command.Split(SpaceSeparator, 2);\n        return ExecuteAsync(cmd[0], cmd.Length > 1 ? cmd[1] : null, workingDirectory, configure, cancellationToken);\n    }\n\n    /// <summary>\n    /// Execute command and capture output async\n    /// </summary>\n    /// <param name=\"command\">command with arguments</param>\n    /// <param name=\"workingDirectory\">working directory for the command</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>command execute result</returns>\n    public static Task<CommandResult> ExecuteCommandAndCaptureAsync(string command, string? workingDirectory = null, Action<ProcessStartInfo>? configure = null, CancellationToken cancellationToken = default)\n    {\n        Guard.NotNullOrEmpty(command);\n        var cmd = command.Split(SpaceSeparator, 2);\n        return ExecuteAndCaptureAsync(cmd[0], cmd.Length > 1 ? cmd[1] : null, workingDirectory, configure, cancellationToken);\n    }\n\n    /// <summary>\n    /// Execute command with a process\n    /// </summary>\n    /// <param name=\"command\">executable command and argument</param>\n    /// <param name=\"arguments\">command arguments</param>\n    /// <param name=\"stdout\">stdout writer, write to console by default</param>\n    /// <param name=\"stderr\">stderr writer, write to console by default</param>\n    /// <param name=\"workingDirectory\">working directory</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>exit code</returns>\n    public static Task<int> ExecuteCommandAndOutputAsync(string command, string? arguments = null,\n        TextWriter? stdout = null, TextWriter? stderr = null,\n        string? workingDirectory = null, Action<ProcessStartInfo>? configure = null, CancellationToken cancellationToken = default)\n    {\n        Guard.NotNullOrEmpty(command);\n        var cmd = command.Split(SpaceSeparator, 2);\n        return ExecuteAndOutputAsync(cmd[0], cmd.Length > 1 ? cmd[1] : null, stdout, stderr, workingDirectory, configure, cancellationToken);\n    }\n\n    /// <summary>\n    /// Execute command with a process\n    /// </summary>\n    /// <param name=\"commandPath\">executable command path</param>\n    /// <param name=\"arguments\">command arguments</param>\n    /// <param name=\"workingDirectory\">working directory</param>\n    /// <param name=\"configure\">configure ProcessStartInfo</param>\n    /// <returns>exit code</returns>\n    public static int Execute(string commandPath, string? arguments = null, string? workingDirectory = null, Action<ProcessStartInfo>? configure = null)\n    {\n        var processStartInfo = new ProcessStartInfo(commandPath, arguments ?? string.Empty)\n        {\n            UseShellExecute = false,\n            CreateNoWindow = true,\n            WorkingDirectory = workingDirectory ?? Environment.CurrentDirectory\n        };\n        configure?.Invoke(processStartInfo);\n        return processStartInfo.Execute();\n    }\n\n    /// <summary>\n    /// Execute command with a process async\n    /// </summary>\n    /// <param name=\"commandPath\">executable command path</param>\n    /// <param name=\"arguments\">command arguments</param>\n    /// <param name=\"workingDirectory\">working directory</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>exit code</returns>\n    public static async Task<int> ExecuteAsync(string commandPath, string? arguments = null, string? workingDirectory = null, Action<ProcessStartInfo>? configure = null, CancellationToken cancellationToken = default)\n    {\n        var processStartInfo = new ProcessStartInfo(commandPath, arguments ?? string.Empty)\n        {\n            UseShellExecute = false,\n            CreateNoWindow = true,\n            WorkingDirectory = workingDirectory ?? Environment.CurrentDirectory\n        };\n        configure?.Invoke(processStartInfo);\n        return await processStartInfo.ExecuteAsync(cancellationToken);\n    }\n\n    /// <summary>\n    /// Execute command with a process\n    /// </summary>\n    /// <param name=\"commandPath\">executable command path</param>\n    /// <param name=\"arguments\">command arguments</param>\n    /// <param name=\"stdout\">stdout writer, write to console by default</param>\n    /// <param name=\"stderr\">stderr writer, write to console by default</param>\n    /// <param name=\"workingDirectory\">working directory</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <returns>exit code</returns>\n    public static int ExecuteAndOutput(string commandPath, string? arguments = null,\n        TextWriter? stdout = null, TextWriter? stderr = null,\n        string? workingDirectory = null, Action<ProcessStartInfo>? configure = null)\n    {\n        var processStartInfo = new ProcessStartInfo(commandPath, arguments ?? string.Empty)\n        {\n            UseShellExecute = false,\n            CreateNoWindow = true,\n            WorkingDirectory = workingDirectory ?? Environment.CurrentDirectory\n        };\n        configure?.Invoke(processStartInfo);\n        return processStartInfo.GetExitCode(stdout ?? Console.Out, stderr ?? Console.Error);\n    }\n\n    /// <summary>\n    /// Execute command with a process\n    /// </summary>\n    /// <param name=\"commandPath\">executable command path</param>\n    /// <param name=\"arguments\">command arguments</param>\n    /// <param name=\"stdout\">stdout writer, write to console by default</param>\n    /// <param name=\"stderr\">stderr writer, write to console by default</param>\n    /// <param name=\"workingDirectory\">working directory</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>exit code</returns>\n    public static async Task<int> ExecuteAndOutputAsync(string commandPath, string? arguments = null,\n        TextWriter? stdout = null, TextWriter? stderr = null,\n        string? workingDirectory = null, Action<ProcessStartInfo>? configure = null, CancellationToken cancellationToken = default)\n    {\n        var processStartInfo = new ProcessStartInfo(commandPath, arguments ?? string.Empty)\n        {\n            UseShellExecute = false,\n            CreateNoWindow = true,\n            WorkingDirectory = workingDirectory ?? Environment.CurrentDirectory\n        };\n        configure?.Invoke(processStartInfo);\n        return await processStartInfo.GetExitCodeAsync(stdout ?? Console.Out, stderr ?? Console.Error, cancellationToken);\n    }\n\n    /// <summary>\n    /// Execute command with a process and capture the output\n    /// </summary>\n    /// <param name=\"commandPath\">executable command path</param>\n    /// <param name=\"arguments\">command arguments</param>\n    /// <param name=\"workingDirectory\">working directory</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <returns>command execute result</returns>\n    public static CommandResult ExecuteAndCapture(string commandPath, string? arguments = null, string? workingDirectory = null, Action<ProcessStartInfo>? configure = null)\n    {\n        var processStartInfo = new ProcessStartInfo(commandPath, arguments ?? string.Empty)\n        {\n            UseShellExecute = false,\n            CreateNoWindow = true,\n            WorkingDirectory = workingDirectory ?? Environment.CurrentDirectory\n        };\n        configure?.Invoke(processStartInfo);\n        return ExecuteAndCapture(processStartInfo);\n    }\n\n    /// <summary>\n    /// Execute command with a process and capture the output\n    /// </summary>\n    /// <param name=\"processStartInfo\">processStartInfo</param>\n    /// <returns>command execute result</returns>\n    public static CommandResult ExecuteAndCapture(this ProcessStartInfo processStartInfo)\n    {\n        return processStartInfo.GetResult();\n    }\n\n    /// <summary>\n    /// Execute command with a process and capture the output\n    /// </summary>\n    /// <param name=\"commandPath\">executable command path</param>\n    /// <param name=\"arguments\">command arguments</param>\n    /// <param name=\"workingDirectory\">working directory</param>\n    /// <param name=\"configure\">configure the ProcessStartInfo</param>\n    /// <param name=\"cancellationToken\">cancellationToken</param>\n    /// <returns>command execute result</returns>\n    public static Task<CommandResult> ExecuteAndCaptureAsync(string commandPath, string? arguments = null, string? workingDirectory = null, Action<ProcessStartInfo>? configure = null, CancellationToken cancellationToken = default)\n    {\n        var processStartInfo = new ProcessStartInfo(commandPath, arguments ?? string.Empty)\n        {\n            UseShellExecute = false,\n            CreateNoWindow = true,\n            WorkingDirectory = workingDirectory ?? Environment.CurrentDirectory\n        };\n        configure?.Invoke(processStartInfo);\n        return ExecuteAndCaptureAsync(processStartInfo, cancellationToken);\n    }\n\n    /// <summary>\n    /// Execute command with a process and capture the output\n    /// </summary>\n    /// <param name=\"processStartInfo\">processStartInfo</param>\n    /// <param name=\"cancellationToken\"></param>\n    /// <returns>command execute result</returns>\n    public static async Task<CommandResult> ExecuteAndCaptureAsync(this ProcessStartInfo processStartInfo, CancellationToken cancellationToken = default)\n    {\n        return await processStartInfo.GetResultAsync(cancellationToken);\n    }\n}\n\npublic sealed class CommandResult(int exitCode, string standardOut, string standardError)\n{\n    public string StandardOut { get; } = standardOut;\n    public string StandardError { get; } = standardError;\n    public int ExitCode { get; } = exitCode;\n    public int? ProcessId { get; set; }\n\n    public CommandResult EnsureSuccessExitCode(int successCode = 0)\n    {\n        ExitCode.EnsureSuccessExitCode(successCode);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/CommandLineParser.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Text;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class CommandLineParser\n{\n    public static IEnumerable<string> ParseLine(string line, LineParseOptions? options = null)\n    {\n        if (string.IsNullOrEmpty(line))\n        {\n            yield break;\n        }\n\n        options ??= new();\n        var tokenBuilder = new StringBuilder();\n\n        var inToken = false;\n        var inQuotes = false;\n\n        // Iterate through every character in the line\n        for (var i = 0; i < line.Length; i++)\n        {\n            var character = line[i];\n\n            // If we are not currently inside a column\n            if (!inToken)\n            {\n                // If the current character is a quote then the token value is contained within\n                // quotes, otherwise append the next character\n                inToken = true;\n                if (character == options.Quote)\n                {\n                    inQuotes = true;\n                    continue;\n                }\n            }\n\n            // If we are in between quotes\n            if (inQuotes)\n            {\n                if (i + 1 == line.Length)\n                {\n                    break;\n                }\n\n                if (character == options.Quote && line[i + 1] == options.Separator) // quotes end\n                {\n                    inQuotes = false;\n                    inToken = false;\n                    i++; //skip next\n                }\n                else if (character == options.Quote && line[i + 1] == options.Quote) // quotes\n                {\n                    i++; //skip next\n                }\n                else if (character == options.Quote)\n                {\n                    throw new ArgumentException($\"unable to escape {line}\");\n                }\n            }\n            else if (character == options.Separator)\n            {\n                inToken = false;\n            }\n\n            // If we are no longer in the token clear the builder and add the columns to the list\n            if (!inToken)\n            {\n                if (!options.RemoveEmptyToken || tokenBuilder.Length > 0)\n                {\n                    yield return tokenBuilder.ToString();\n                    tokenBuilder.Clear();\n                }\n            }\n            else\n            {\n                tokenBuilder.Append(character);\n            }\n        }\n\n        if (!options.RemoveEmptyToken || tokenBuilder.Length > 0)\n        {\n            yield return tokenBuilder.ToString();\n            tokenBuilder.Clear();\n        }\n    }\n\n    /// <summary>\n    /// Get argument value from arguments\n    /// </summary>\n    /// <param name=\"optionName\">argument name to get value</param>\n    /// <param name=\"args\">arguments</param>\n    /// <param name=\"defaultValue\">default argument value when not found</param>\n    /// <returns>argument value</returns>\n    [return: NotNullIfNotNull(nameof(defaultValue))]\n    public static string? Val(string optionName, string[]? args = null, string? defaultValue = default)\n    {\n        return GetValueInternal(args ?? Environment.GetCommandLineArgs(), optionName) ?? defaultValue;\n    }\n\n    /// <summary>\n    /// Get argument value from arguments\n    /// </summary>\n    /// <param name=\"optionName\">argument name to get value</param>\n    /// <param name=\"defaultValue\">default argument value when not found</param>\n    /// <param name=\"args\">arguments</param>\n    /// <returns>argument value</returns>\n    [return: NotNullIfNotNull(nameof(defaultValue))]\n    public static string? Val(string optionName, string? defaultValue, string[]? args = null)\n    {\n        return GetValueInternal(args ?? Environment.GetCommandLineArgs(), optionName) ?? defaultValue;\n    }\n\n    /// <summary>\n    /// Get argument value from arguments\n    /// </summary>\n    /// <param name=\"args\">arguments</param>\n    /// <param name=\"defaultValue\">default argument value when not found</param>\n    /// <param name=\"optionName\">argument name to get value</param>\n    /// <returns>argument value</returns>\n    [return: NotNullIfNotNull(nameof(defaultValue))]\n    public static string? Val(string[] args, string optionName, string? defaultValue = null)\n    {\n        return GetValueInternal(args, optionName) ?? defaultValue;\n    }\n\n    /// <summary>\n    /// Get boolean argument value from arguments\n    /// </summary>\n    /// <param name=\"optionName\">argument name to get value</param>\n    /// <param name=\"args\">arguments</param>\n    /// <param name=\"defaultValue\">default argument value when not found</param>    \n    /// <returns>Boolean value</returns>\n    public static bool BooleanVal(string optionName, string[]? args = null, bool defaultValue = default)\n    {\n        return GetValueInternal(args ?? Environment.GetCommandLineArgs(), optionName).ToBoolean(defaultValue);\n    }\n\n    /// <summary>\n    /// Get boolean argument value from arguments\n    /// </summary>    \n    /// <param name=\"optionName\">argument name to get value</param>\n    /// <param name=\"defaultValue\">default argument value when not found</param>\n    /// <param name=\"args\">arguments</param>\n    /// <returns>Boolean value</returns>\n    public static bool BooleanVal(string optionName, bool defaultValue, string[]? args = null)\n    {\n        return GetValueInternal(args ?? Environment.GetCommandLineArgs(), optionName).ToBoolean(defaultValue);\n    }\n\n    /// <summary>\n    /// Get boolean argument value from arguments\n    /// </summary>    \n    /// <param name=\"args\">arguments</param>\n    /// <param name=\"optionName\">argument name to get value</param>\n    /// <param name=\"defaultValue\">default argument value when not found</param>    \n    /// <returns>Boolean value</returns>\n    public static bool BooleanVal(string[] args, string optionName, bool defaultValue = default)\n    {\n        return GetValueInternal(args, optionName).ToBoolean(defaultValue);\n    }\n\n    private static string? GetValueInternal(string[] args, string argumentName)\n    {\n        Guard.NotNull(args);\n        Guard.NotNullOrEmpty(argumentName);\n        for (var i = 0; i < args.Length; i++)\n        {\n            if (args[i] == $\"--{argumentName}\" || args[i] == $\"-{argumentName}\")\n            {\n                if (i + 1 == args.Length || args[i + 1].StartsWith('-'))\n                    return string.Empty;\n\n                return args[i + 1];\n            }\n\n            if (args[i].StartsWith($\"-{argumentName}=\", StringComparison.Ordinal)\n                || args[i].StartsWith($\"-{argumentName}:\", StringComparison.Ordinal))\n                return args[i][$\"-{argumentName}=\".Length..];\n\n            if (args[i].StartsWith($\"--{argumentName}=\", StringComparison.Ordinal)\n                || args[i].StartsWith($\"--{argumentName}:\", StringComparison.Ordinal))\n                return args[i][$\"--{argumentName}=\".Length..];\n        }\n\n        return null;\n    }\n}\n\npublic sealed class LineParseOptions\n{\n    public char Separator { get; set; } = ' ';\n    public char Quote { get; set; } = '\\'';\n    public bool RemoveEmptyToken { get; set; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ConcurrentSet.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Concurrent;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic sealed class ConcurrentSet<T> : IReadOnlyCollection<T>, ICollection<T> where T : notnull\n{\n    // https://github.com/dotnet/runtime/issues/39919#issuecomment-954774092\n    private readonly ConcurrentDictionary<T, byte> _dictionary = new();\n\n    public bool IsEmpty => _dictionary.IsEmpty;\n\n    public int Count => _dictionary.Count;\n\n    public bool IsReadOnly => false;\n\n    public bool Contains(T item) => _dictionary.ContainsKey(item);\n\n    public bool TryAdd(T t) => _dictionary.TryAdd(t, default);\n\n    public bool TryRemove(T t) => _dictionary.TryRemove(t, out _);\n\n    public ICollection<T> Values() => _dictionary.Keys;\n\n    public void Clear() => _dictionary.Clear();\n\n    public IEnumerator<T> GetEnumerator()\n    {\n        return _dictionary.Keys.GetEnumerator();\n    }\n\n    IEnumerator IEnumerable.GetEnumerator()\n    {\n        return GetEnumerator();\n    }\n\n    public void Add(T item)\n    {\n        TryAdd(item);\n    }\n\n    public void CopyTo(T[] array, int arrayIndex)\n    {\n        _dictionary.Keys.CopyTo(array, arrayIndex);\n    }\n\n    public bool Remove(T item)\n    {\n        return TryRemove(item);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ConsoleHelper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Runtime.InteropServices;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class ConsoleHelper\n{\n    // Cache the result of ANSI color support detection since it doesn't change during execution\n    private static readonly Lazy<bool> _supportsAnsiColors = new Lazy<bool>(DetectAnsiColorSupport);\n\n    public static void InvokeWithConsoleColor(Action action, ConsoleColor? foregroundColor,\n        ConsoleColor? backgroundColor = null)\n    {\n        Guard.NotNull(action);\n        var originalForegroundColor = Console.ForegroundColor;\n        var originalBackgroundColor = Console.BackgroundColor;\n        try\n        {\n            if (foregroundColor.HasValue)\n            {\n                Console.ForegroundColor = foregroundColor.Value;\n            }\n\n            if (backgroundColor.HasValue)\n            {\n                Console.BackgroundColor = backgroundColor.Value;\n            }\n\n            action();\n        }\n        finally\n        {\n            Console.ForegroundColor = originalForegroundColor;\n            Console.BackgroundColor = originalBackgroundColor;\n        }\n    }\n\n    public static async Task InvokeWithConsoleColor(Func<Task> action, ConsoleColor? foregroundColor,\n        ConsoleColor? backgroundColor = null)\n    {\n        Guard.NotNull(action);\n        var originalForegroundColor = Console.ForegroundColor;\n        var originalBackgroundColor = Console.BackgroundColor;\n        try\n        {\n            if (foregroundColor.HasValue)\n            {\n                Console.ForegroundColor = foregroundColor.Value;\n            }\n\n            if (backgroundColor.HasValue)\n            {\n                Console.BackgroundColor = backgroundColor.Value;\n            }\n\n            await action();\n        }\n        finally\n        {\n            Console.ForegroundColor = originalForegroundColor;\n            Console.BackgroundColor = originalBackgroundColor;\n        }\n    }\n\n    public static void WriteWithColor(string? output, ConsoleColor? foregroundColor, ConsoleColor? backgroundColor = null)\n    {\n        if (!string.IsNullOrEmpty(output))\n        {\n            if (SupportsAnsiColors())\n            {\n                var coloredOutput = ApplyAnsiColor(output!, foregroundColor, backgroundColor);\n                Console.Write(coloredOutput);\n            }\n            else\n            {\n                InvokeWithConsoleColor(() => Console.Write(output), foregroundColor, backgroundColor);\n            }\n        }\n    }\n\n    public static void WriteLineWithColor(string? output, ConsoleColor? foregroundColor, ConsoleColor? backgroundColor = null)\n    {\n        if (string.IsNullOrEmpty(output))\n            Console.WriteLine();\n        else\n        {\n            if (SupportsAnsiColors())\n            {\n                var coloredOutput = ApplyAnsiColor(output!, foregroundColor, backgroundColor);\n                Console.WriteLine(coloredOutput);\n            }\n            else\n            {\n                InvokeWithConsoleColor(() => Console.WriteLine(output), foregroundColor, backgroundColor);\n            }\n        }\n    }\n\n    public static void ErrorWriteWithColor(string? output, ConsoleColor? foregroundColor, ConsoleColor? backgroundColor = null)\n    {\n        if (!string.IsNullOrEmpty(output))\n        {\n            if (SupportsAnsiColors())\n            {\n                var coloredOutput = ApplyAnsiColor(output!, foregroundColor, backgroundColor);\n                Console.Error.Write(coloredOutput);\n            }\n            else\n            {\n                InvokeWithConsoleColor(() => Console.Error.Write(output), foregroundColor, backgroundColor);\n            }\n        }\n    }\n\n    public static void ErrorWriteLineWithColor(string? error, ConsoleColor? foregroundColor, ConsoleColor? backgroundColor = null)\n    {\n        if (string.IsNullOrEmpty(error))\n            Console.Error.WriteLine();\n        else\n        {\n            if (SupportsAnsiColors())\n            {\n                var coloredOutput = ApplyAnsiColor(error!, foregroundColor, backgroundColor);\n                Console.Error.WriteLine(coloredOutput);\n            }\n            else\n            {\n                InvokeWithConsoleColor(() => Console.Error.WriteLine(error), foregroundColor, backgroundColor);\n            }\n        }\n    }\n\n    public static string? GetInput(string? inputPrompt = null, bool insertNewLine = true)\n    {\n        var input = ReadLineWithPrompt(inputPrompt);\n        if (insertNewLine)\n        {\n            Console.WriteLine();\n        }\n        return input;\n    }\n\n    public static async Task HandleInputLoopAsync(Func<string, Task<bool>> handler,\n        string? inputPrompt = null,\n        string exitInput = \"q\",\n        bool insertNewLine = true)\n    {\n        Guard.NotNull(handler);\n        var input = GetInput(inputPrompt, insertNewLine);\n        while (input != exitInput)\n        {\n            var result = await handler(input ?? string.Empty);\n            if (!result) break;\n\n            input = GetInput(inputPrompt);\n        }\n    }\n\n    public static async Task HandleInputLoopAsync(Func<string, Task> handler,\n        string? inputPrompt = null,\n        string exitInput = \"q\",\n        bool insertNewLine = true)\n    {\n        Guard.NotNull(handler);\n        await HandleInputLoopAsync(async input =>\n        {\n            await handler.Invoke(input);\n            return true;\n        }, inputPrompt, exitInput, insertNewLine);\n    }\n\n    public static void HandleInputLoop(Func<string, bool> handler,\n        string? inputPrompt = null,\n        string exitInput = \"q\",\n        bool insertNewLine = true)\n    {\n        Guard.NotNull(handler);\n        var input = GetInput(inputPrompt, insertNewLine);\n        while (input != exitInput)\n        {\n            var result = handler(input ?? string.Empty);\n            if (!result) break;\n\n            input = GetInput(inputPrompt, insertNewLine);\n        }\n    }\n\n    public static void HandleInputLoop(Action<string> handler,\n        string? inputPrompt = null,\n        string exitInput = \"q\",\n        bool insertNewLine = true)\n    {\n        Guard.NotNull(handler);\n        HandleInputLoop(input =>\n        {\n            handler.Invoke(input);\n            return true;\n        }, inputPrompt, exitInput, insertNewLine);\n    }\n\n    public static string? ReadLineWithPrompt(string? prompt = \"Press Enter to continue\")\n    {\n        if (prompt is not null) Console.WriteLine(prompt);\n        return Console.ReadLine();\n    }\n\n    public static ConsoleKeyInfo ReadKeyWithPrompt(string? prompt = \"Press any key to continue\")\n    {\n        if (prompt is not null) Console.WriteLine(prompt);\n        return Console.ReadKey();\n    }\n\n    public static void WriteLineIf(string? output, bool condition)\n    {\n        if (condition) Console.WriteLine(output);\n    }\n\n    public static void WriteIf(string? output, bool condition)\n    {\n        if (condition) Console.Write(output);\n    }\n\n    public static void ErrorWriteLineIf(string? output, bool condition)\n    {\n        if (condition) Console.Error.WriteLine(output);\n    }\n\n    public static void ErrorWriteIf(string? output, bool condition)\n    {\n        if (condition) Console.Error.Write(output);\n    }\n\n    public static CommandResult PrintOutputToConsole(this CommandResult commandResult)\n    {\n        Guard.NotNull(commandResult);\n\n        Console.WriteLine(commandResult.StandardOut);\n\n        if (!string.IsNullOrEmpty(commandResult.StandardError))\n        {\n            Console.WriteLine();\n            ErrorWriteLineWithColor(commandResult.StandardError, ConsoleColor.DarkRed);\n        }\n\n        return commandResult;\n    }\n\n    public static bool HasStandardInput()\n    {\n        return Console.IsInputRedirected && Console.In.Peek() != -1;\n    }\n\n    public static bool TryGetStandardInput([MaybeNullWhen(false)] out string input)\n    {\n        if (HasStandardInput())\n        {\n            input = Console.In.ReadToEnd();\n            return true;\n        }\n\n        input = null;\n        return false;\n    }\n\n    /// <summary>\n    /// Determines whether the console supports ANSI escape sequences for color output.\n    /// </summary>\n    /// <returns>true if ANSI colors are supported; otherwise, false.</returns>\n    public static bool SupportsAnsiColors() => _supportsAnsiColors.Value;\n\n    private static bool DetectAnsiColorSupport()\n    {\n        // Only when output is actually a terminal\n        if (Console.IsOutputRedirected)\n            return false;\n\n        // Check for explicit environment variable to disable ANSI colors\n        var noColor = Environment.GetEnvironmentVariable(\"NO_COLOR\");\n        if (!string.IsNullOrEmpty(noColor) || Environment.GetEnvironmentVariable(\"CLICOLOR\") == \"0\")\n            return false;\n\n        // On Windows, check for Virtual Terminal Processing support\n        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n        {\n            // Windows Terminal and newer Windows versions support ANSI\n            var wtSession = Environment.GetEnvironmentVariable(\"WT_SESSION\");\n            if (!string.IsNullOrEmpty(wtSession))\n                return true;\n\n            // Check Windows version - Windows 10 build 10586 and later support VT\n            try\n            {\n                var version = Environment.OSVersion.Version;\n                if (version.Major >= 10 && version.Build >= 10586)\n                    return true;\n            }\n            catch (PlatformNotSupportedException)\n            {\n                // Platform doesn't support version detection\n                return false;\n            }\n\n            return false;\n        }\n\n        // On Unix-like systems, check the TERM environment variable\n        var term = Environment.GetEnvironmentVariable(\"TERM\");\n\n        // Return false if TERM is not set or is \"dumb\"\n        if (string.IsNullOrEmpty(term) || term == \"dumb\")\n            return false;\n\n        // Common terminal types that support ANSI colors\n        return true;\n    }\n\n    private static string ApplyAnsiColor(string text, ConsoleColor? foregroundColor, ConsoleColor? backgroundColor = null)\n    {\n        if (!foregroundColor.HasValue && !backgroundColor.HasValue)\n            return text;\n\n        var hasForeground = foregroundColor.HasValue;\n        var hasBackground = backgroundColor.HasValue;\n\n        var fgCode = 0;\n        var bgCode = 0;\n        if (hasForeground)\n        {\n            fgCode = GetAnsiColorCode(foregroundColor!.Value, isForeground: true);\n        }\n        if (hasBackground)\n        {\n            bgCode = GetAnsiColorCode(backgroundColor!.Value, isForeground: false);\n        }\n\n        var ansiStart = (hasForeground, hasBackground) switch\n        {\n            (true, true) => $\"\\e[{fgCode};{bgCode}m\",\n            (true, false) => $\"\\e[{fgCode}m\",\n            _ => $\"\\e[{bgCode}m\"\n        };\n        var ansiReset = \"\\e[0m\";\n\n        return $\"{ansiStart}{text}{ansiReset}\";\n    }\n\n    private static int GetAnsiColorCode(ConsoleColor color, bool isForeground)\n    {\n        return color switch\n        {\n            // Standard colors (30-37 for foreground, 40-47 for background)\n            ConsoleColor.Black => isForeground ? 30 : 40,\n            ConsoleColor.DarkRed => isForeground ? 31 : 41,\n            ConsoleColor.DarkGreen => isForeground ? 32 : 42,\n            ConsoleColor.DarkYellow => isForeground ? 33 : 43,\n            ConsoleColor.DarkBlue => isForeground ? 34 : 44,\n            ConsoleColor.DarkMagenta => isForeground ? 35 : 45,\n            ConsoleColor.DarkCyan => isForeground ? 36 : 46,\n            ConsoleColor.Gray => isForeground ? 37 : 47,\n            // Bright colors (90-97 for foreground, 100-107 for background)\n            ConsoleColor.DarkGray => isForeground ? 90 : 100,\n            ConsoleColor.Red => isForeground ? 91 : 101,\n            ConsoleColor.Green => isForeground ? 92 : 102,\n            ConsoleColor.Yellow => isForeground ? 93 : 103,\n            ConsoleColor.Blue => isForeground ? 94 : 104,\n            ConsoleColor.Magenta => isForeground ? 95 : 105,\n            ConsoleColor.Cyan => isForeground ? 96 : 106,\n            ConsoleColor.White => isForeground ? 97 : 107,\n            _ => isForeground ? 37 : 47 // Default to gray\n        };\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ConsoleOutput.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Helpers;\n\npublic sealed class ConsoleOutput : IDisposable\n{\n    private TextWriter? _originalOutputWriter;\n    private TextWriter? _originalErrorWriter;\n    private readonly StringWriter _outputWriter = new();\n    private readonly StringWriter _errorWriter = new();\n\n    private const int NotDisposed = 0;\n    private const int Disposed = 1;\n\n    private int _alreadyDisposed = NotDisposed;\n\n    private static readonly SemaphoreSlim ConsoleLock = new(1, 1);\n\n    private ConsoleOutput()\n    {\n    }\n\n    public static ConsoleOutput Capture()\n    {\n        ConsoleLock.Wait();\n        return GetConsoleOutputInternal();\n    }\n\n    public static async Task<ConsoleOutput> CaptureAsync()\n    {\n        await ConsoleLock.WaitAsync();\n        return GetConsoleOutputInternal();\n    }\n\n    public void Dispose()\n    {\n        if (Interlocked.CompareExchange(ref _alreadyDisposed, Disposed, NotDisposed) == NotDisposed)\n        {\n            if (_originalOutputWriter != null)\n            {\n                Console.SetOut(_originalOutputWriter);\n            }\n            if (_originalErrorWriter != null)\n            {\n                Console.SetError(_originalErrorWriter);\n            }\n            if (ConsoleLock.CurrentCount < 1)\n            {\n                ConsoleLock.Release();\n            }\n        }\n    }\n\n    public string StandardOutput => _outputWriter.ToString();\n\n    public string StandardError => _errorWriter.ToString();\n\n    public void Clear()\n    {\n        _outputWriter.GetStringBuilder().Clear();\n        _errorWriter.GetStringBuilder().Clear();\n    }\n\n    private static ConsoleOutput GetConsoleOutputInternal()\n    {\n        var outputCapture = new ConsoleOutput();\n        try\n        {\n            outputCapture._originalOutputWriter = Console.Out;\n            outputCapture._originalErrorWriter = Console.Error;\n\n            Console.SetOut(outputCapture._outputWriter);\n            Console.SetError(outputCapture._errorWriter);\n        }\n        catch\n        {\n            ConsoleLock.Release();\n            throw;\n        }\n\n        return outputCapture;\n\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ContextAccessor.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Helpers;\n\npublic sealed class ContextAccessor<TContext> where TContext : class\n{\n    private static readonly AsyncLocal<ContextHolder<TContext>> ContextCurrent = new();\n\n    public static TContext? Context\n    {\n        get\n        {\n            return ContextCurrent.Value?.Value;\n        }\n        set\n        {\n            var holder = ContextCurrent.Value;\n            if (holder != null)\n            {\n                // Clear current Value trapped in the AsyncLocals, as its done.\n                holder.Value = null;\n            }\n            if (value != null)\n            {\n                // Use an object indirection to hold the Value in the AsyncLocal,\n                // so it can be cleared in all ExecutionContexts when its cleared.\n                ContextCurrent.Value = new ContextHolder<TContext>\n                {\n                    Value = value\n                };\n            }\n        }\n    }\n\n    private sealed class ContextHolder<TContext1> where TContext1 : class\n    {\n        public TContext1? Value;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ConvertHelper.cs",
    "content": "﻿using System.Net;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class ConvertHelper\n{\n    public static EndPoint ToEndPoint(string ipOrHost, int port)\n    {\n        if (IPAddress.TryParse(ipOrHost, out var address))\n        {\n            return new IPEndPoint(address, port);\n        }\n        return new DnsEndPoint(ipOrHost, port);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Cron/CalendarHelper.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// See the LICENSE file in the project root for more information.\n\nusing System.Runtime.CompilerServices;\n\nnamespace WeihanLi.Common.Helpers.Cron;\n\ninternal static class CalendarHelper\n{\n    private const int DaysPerWeekCount = 7;\n\n    private const long TicksPerMillisecond = 10000;\n    private const long TicksPerSecond = TicksPerMillisecond * 1000;\n    private const long TicksPerMinute = TicksPerSecond * 60;\n    private const long TicksPerHour = TicksPerMinute * 60;\n    private const long TicksPerDay = TicksPerHour * 24;\n\n    // Number of days in a non-leap year\n    private const int DaysPerYear = 365;\n\n    // Number of days in 4 years\n    private const int DaysPer4Years = DaysPerYear * 4 + 1;       // 1461\n\n    // Number of days in 100 years\n    private const int DaysPer100Years = DaysPer4Years * 25 - 1;  // 36524\n\n    // Number of days in 400 years\n    private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097\n\n    private static readonly int[] DaysToMonth365 =\n    [\n        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365\n    ];\n\n    private static readonly int[] DaysToMonth366 =\n    [\n        0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366\n    ];\n\n    private static readonly int[] DaysInMonth =\n    [\n        -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31\n    ];\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public static bool IsGreaterThan(int year1, int month1, int day1, int year2, int month2, int day2)\n    {\n        if (year1 != year2) return year1 > year2;\n        if (month1 != month2) return month1 > month2;\n        if (day2 != day1) return day1 > day2;\n        return false;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public static long DateTimeToTicks(int year, int month, int day, int hour, int minute, int second)\n    {\n        var days = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) ? DaysToMonth366 : DaysToMonth365;\n        var y = year - 1;\n        var n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;\n        return n * TicksPerDay + (hour * 3600L + minute * 60L + second) * TicksPerSecond;\n    }\n\n    // Returns a given date part of this DateTime. This method is used\n    // to compute the year, day-of-year, month, or day part.\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public static void FillDateTimeParts(long ticks, out int second, out int minute, out int hour,\n        out int day, out int month, out int year)\n    {\n        second = (int)(ticks / TicksPerSecond % 60);\n        if (ticks % TicksPerSecond != 0) second++;\n        minute = (int)(ticks / TicksPerMinute % 60);\n        hour = (int)(ticks / TicksPerHour % 24);\n\n        // n = number of days since 1/1/0001\n        var n = (int)(ticks / TicksPerDay);\n        // y400 = number of whole 400-year periods since 1/1/0001\n        var y400 = n / DaysPer400Years;\n        // n = day number within 400-year period\n        n -= y400 * DaysPer400Years;\n        // y100 = number of whole 100-year periods within 400-year period\n        var y100 = n / DaysPer100Years;\n        // Last 100-year period has an extra day, so decrement result if 4\n        if (y100 == 4) y100 = 3;\n        // n = day number within 100-year period\n        n -= y100 * DaysPer100Years;\n        // y4 = number of whole 4-year periods within 100-year period\n        var y4 = n / DaysPer4Years;\n        // n = day number within 4-year period\n        n -= y4 * DaysPer4Years;\n        // y1 = number of whole years within 4-year period\n        var y1 = n / DaysPerYear;\n        // Last year has an extra day, so decrement result if 4\n        if (y1 == 4) y1 = 3;\n        // If year was requested, compute and return it\n        year = y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1;\n        // n = day number within year\n        n -= y1 * DaysPerYear;\n        // Leap year calculation looks different from IsLeapYear since y1, y4,\n        // and y100 are relative to year 1, not year 0\n        var leapYear = y1 == 3 && (y4 != 24 || y100 == 3);\n        var days = leapYear ? DaysToMonth366 : DaysToMonth365;\n        // All months have less than 32 days, so n >> 5 is a good conservative\n        // estimate for the month\n        month = (n >> 5) + 1;\n        // m = 1-based month number\n\n        // day = 1-based day-of-month\n        while (n >= days[month]) month++;\n        day = n - days[month - 1] + 1;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public static DayOfWeek GetDayOfWeek(int year, int month, int day)\n    {\n        var isLeapYear = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);\n        var days = isLeapYear ? DaysToMonth366 : DaysToMonth365;\n        var y = year - 1;\n        var n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;\n        var ticks = n * TicksPerDay;\n\n        return ((DayOfWeek)((int)(ticks / TicksPerDay + 1) % 7));\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public static int GetDaysInMonth(int year, int month)\n    {\n        if (month != 2 || year % 4 != 0) return DaysInMonth[month];\n\n        return year % 100 != 0 || year % 400 == 0 ? 29 : 28;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public static int MoveToNearestWeekDay(int year, int month, int day)\n    {\n        var dayOfWeek = GetDayOfWeek(year, month, day);\n\n        if (dayOfWeek != DayOfWeek.Saturday && dayOfWeek != DayOfWeek.Sunday) return day;\n\n        return dayOfWeek == DayOfWeek.Sunday\n            ? day == GetDaysInMonth(year, month)\n                ? day - 2\n                : day + 1\n            : day == CronField.DaysOfMonth.First\n                ? day + 2\n                : day - 1;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public static bool IsNthDayOfWeek(int day, int n)\n    {\n        return day - DaysPerWeekCount * n < CronField.DaysOfMonth.First &&\n               day - DaysPerWeekCount * (n - 1) >= CronField.DaysOfMonth.First;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public static bool IsLastDayOfWeek(int year, int month, int day)\n    {\n        return day + DaysPerWeekCount > GetDaysInMonth(year, month);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Cron/CronExpression.cs",
    "content": "﻿// The MIT License(MIT)\n//\n// Copyright (c) 2017 Sergey Odinokov\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n#nullable disable\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.CompilerServices;\nusing System.Text;\n\nnamespace WeihanLi.Common.Helpers.Cron;\n\n// https://github.com/HangfireIO/Cronos\n/// <summary>\n/// Provides a parser and scheduler for cron expressions.\n/// </summary>\npublic sealed class CronExpression : IEquatable<CronExpression>\n{\n    private const long NotFound = 0;\n\n    private const int MinNthDayOfWeek = 1;\n    private const int MaxNthDayOfWeek = 5;\n    private const int SundayBits = 0b1000_0001;\n\n    private const int MaxYear = 2099;\n\n    private static readonly TimeZoneInfo UtcTimeZone = TimeZoneInfo.Utc;\n\n    internal static readonly CronExpression Yearly = Parse(\"0 0 1 1 *\");\n    internal static readonly CronExpression Weekly = Parse(\"0 0 * * 0\");\n    internal static readonly CronExpression Monthly = Parse(\"0 0 1 * *\");\n    internal static readonly CronExpression Daily = Parse(\"0 0 * * *\");\n    internal static readonly CronExpression Hourly = Parse(\"0 * * * *\");\n    internal static readonly CronExpression Minutely = Parse(\"* * * * *\");\n    internal static readonly CronExpression Secondly = Parse(\"* * * * * *\", CronFormat.IncludeSeconds);\n\n    private static readonly int[] DeBruijnPositions =\n    [\n        0, 1, 2, 53, 3, 7, 54, 27,\n        4, 38, 41, 8, 34, 55, 48, 28,\n        62, 5, 39, 46, 44, 42, 22, 9,\n        24, 35, 59, 56, 49, 18, 29, 11,\n        63, 52, 6, 26, 37, 40, 33, 47,\n        61, 45, 43, 21, 23, 58, 17, 10,\n        51, 25, 36, 32, 60, 20, 57, 16,\n        50, 31, 19, 15, 30, 14, 13, 12\n    ];\n\n    private long _second;     // 60 bits -> from 0 bit to 59 bit\n    private long _minute;     // 60 bits -> from 0 bit to 59 bit\n    private int _hour;       // 24 bits -> from 0 bit to 23 bit\n    private int _dayOfMonth; // 31 bits -> from 1 bit to 31 bit\n    private short _month;      // 12 bits -> from 1 bit to 12 bit\n    private byte _dayOfWeek;  // 8 bits  -> from 0 bit to 7 bit\n\n    private byte _nthDayOfWeek;\n    private byte _lastMonthOffset;\n\n    private CronExpressionFlag _flags;\n    private static readonly char[] separator = [' '];\n\n    private CronExpression()\n    {\n    }\n\n    ///<summary>\n    /// Constructs a new <see cref=\"CronExpression\"/> based on the specified\n    /// cron expression. It's supported expressions consisting of 5 fields:\n    /// minute, hour, day of month, month, day of week.\n    /// If you want to parse non-standard cron expressions use <see cref=\"Parse(string, CronFormat)\"/> with specified CronFields argument.\n    /// See more: <a href=\"https://github.com/HangfireIO/Cronos\">https://github.com/HangfireIO/Cronos</a>\n    /// </summary>\n    public static CronExpression Parse(string expression)\n    {\n        return expression.Split(separator, StringSplitOptions.RemoveEmptyEntries).Length == 5\n             ? Parse(expression, CronFormat.Standard)\n             : Parse(expression, CronFormat.IncludeSeconds);\n    }\n\n    ///<summary>\n    /// Constructs a new <see cref=\"CronExpression\"/> based on the specified\n    /// cron expression. It's supported expressions consisting of 5 or 6 fields:\n    /// second (optional), minute, hour, day of month, month, day of week.\n    /// See more: <a href=\"https://github.com/HangfireIO/Cronos\">https://github.com/HangfireIO/Cronos</a>\n    /// </summary>\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public static unsafe CronExpression Parse(string expression, CronFormat format)\n    {\n        Guard.NotNullOrEmpty(expression);\n\n        fixed (char* value = expression)\n        {\n            var pointer = value;\n\n            SkipWhiteSpaces(ref pointer);\n\n            CronExpression cronExpression;\n\n            if (Accept(ref pointer, '@'))\n            {\n                cronExpression = ParseMacro(ref pointer);\n                SkipWhiteSpaces(ref pointer);\n\n                if (cronExpression == null || !IsEndOfString(*pointer)) ThrowFormatException(\"Macro: Unexpected character '{0}' on position {1}.\", *pointer, pointer - value);\n\n                return cronExpression;\n            }\n\n            cronExpression = new CronExpression();\n\n            if (format == CronFormat.IncludeSeconds)\n            {\n                cronExpression._second = ParseField(CronField.Seconds, ref pointer, ref cronExpression._flags);\n                ParseWhiteSpace(CronField.Seconds, ref pointer);\n            }\n            else\n            {\n                SetBit(ref cronExpression._second, CronField.Seconds.First);\n            }\n\n            cronExpression._minute = ParseField(CronField.Minutes, ref pointer, ref cronExpression._flags);\n            ParseWhiteSpace(CronField.Minutes, ref pointer);\n\n            cronExpression._hour = (int)ParseField(CronField.Hours, ref pointer, ref cronExpression._flags);\n            ParseWhiteSpace(CronField.Hours, ref pointer);\n\n            cronExpression._dayOfMonth = (int)ParseDayOfMonth(ref pointer, ref cronExpression._flags, ref cronExpression._lastMonthOffset);\n            ParseWhiteSpace(CronField.DaysOfMonth, ref pointer);\n\n            cronExpression._month = (short)ParseField(CronField.Months, ref pointer, ref cronExpression._flags);\n            ParseWhiteSpace(CronField.Months, ref pointer);\n\n            cronExpression._dayOfWeek = (byte)ParseDayOfWeek(ref pointer, ref cronExpression._flags, ref cronExpression._nthDayOfWeek);\n            ParseEndOfString(ref pointer);\n\n            // Make sundays equivalent.\n            if ((cronExpression._dayOfWeek & SundayBits) != 0)\n            {\n                cronExpression._dayOfWeek |= SundayBits;\n            }\n\n            return cronExpression;\n        }\n    }\n\n    /// <summary>\n    /// Calculates next occurrence starting with <paramref name=\"from\"/> (optionally <paramref name=\"inclusive\"/>) in given <paramref name=\"zone\"/>\n    /// </summary>\n    public DateTimeOffset? GetNextOccurrence(DateTimeOffset from, TimeZoneInfo zone, bool inclusive = false)\n    {\n        if (ReferenceEquals(zone, UtcTimeZone))\n        {\n            var found = FindOccurence(from.UtcTicks, inclusive);\n            if (found == NotFound) return null;\n\n            return new DateTimeOffset(found, TimeSpan.Zero);\n        }\n\n        var zonedStart = TimeZoneInfo.ConvertTime(from, zone);\n        return GetOccurenceByZonedTimes(zonedStart, zone, inclusive);\n    }\n\n    /// <summary>\n    /// Returns the list of occurrences within the given date/time offset range,\n    /// including <paramref name=\"from\"/> and excluding <paramref name=\"to\"/> by\n    /// default. When none of the occurrences found, an empty list is returned.\n    /// </summary>\n    public IEnumerable<DateTimeOffset> GetOccurrences(\n        DateTimeOffset from,\n        DateTimeOffset to,\n        TimeZoneInfo zone,\n        bool fromInclusive = true,\n        bool toInclusive = false)\n    {\n        if (from > to) ThrowFromShouldBeLessThanToException(nameof(from), nameof(to));\n\n        for (var occurrence = GetNextOccurrence(from, zone, fromInclusive);\n            occurrence < to || occurrence == to && toInclusive;\n            // ReSharper disable once RedundantArgumentDefaultValue\n            // ReSharper disable once ArgumentsStyleLiteral\n            occurrence = GetNextOccurrence(occurrence.Value, zone, inclusive: false))\n        {\n            yield return occurrence.Value;\n        }\n    }\n\n    public override string ToString()\n    {\n        var expressionBuilder = new StringBuilder();\n\n        AppendFieldValue(expressionBuilder, CronField.Seconds, _second).Append(' ');\n        AppendFieldValue(expressionBuilder, CronField.Minutes, _minute).Append(' ');\n        AppendFieldValue(expressionBuilder, CronField.Hours, _hour).Append(' ');\n        AppendDayOfMonth(expressionBuilder, _dayOfMonth).Append(' ');\n        AppendFieldValue(expressionBuilder, CronField.Months, _month).Append(' ');\n        AppendDayOfWeek(expressionBuilder, _dayOfWeek);\n\n        return expressionBuilder.ToString();\n    }\n\n    /// <summary>\n    /// Determines whether the specified <see cref=\"object\"/> is equal to the current <see cref=\"object\"/>.\n    /// </summary>\n    /// <param name=\"other\">The <see cref=\"object\"/> to compare with the current <see cref=\"object\"/>.</param>\n    /// <returns>\n    /// <c>true</c> if the specified <see cref=\"object\"/> is equal to the current <see cref=\"object\"/>; otherwise, <c>false</c>.\n    /// </returns>\n    public bool Equals(CronExpression other)\n    {\n        if (other == null) return false;\n\n        return _second == other._second &&\n               _minute == other._minute &&\n               _hour == other._hour &&\n               _dayOfMonth == other._dayOfMonth &&\n               _month == other._month &&\n               _dayOfWeek == other._dayOfWeek &&\n               _nthDayOfWeek == other._nthDayOfWeek &&\n               _lastMonthOffset == other._lastMonthOffset &&\n               _flags == other._flags;\n    }\n\n    /// <summary>\n    /// Determines whether the specified <see cref=\"object\" /> is equal to this instance.\n    /// </summary>\n    /// <param name=\"obj\">The <see cref=\"object\" /> to compare with this instance.</param>\n    /// <returns>\n    /// <c>true</c> if the specified <see cref=\"object\" /> is equal to this instance;\n    /// otherwise, <c>false</c>.\n    /// </returns>\n    public override bool Equals(object obj) => Equals(obj as CronExpression);\n\n    /// <summary>\n    /// Returns a hash code for this instance.\n    /// </summary>\n    /// <returns>\n    /// A hash code for this instance, suitable for use in hashing algorithms and data\n    /// structures like a hash table.\n    /// </returns>\n    [SuppressMessage(\"ReSharper\", \"NonReadonlyMemberInGetHashCode\")]\n    public override int GetHashCode()\n    {\n        unchecked\n        {\n            var hashCode = _second.GetHashCode();\n            hashCode = (hashCode * 397) ^ _minute.GetHashCode();\n            hashCode = (hashCode * 397) ^ _hour;\n            hashCode = (hashCode * 397) ^ _dayOfMonth;\n            hashCode = (hashCode * 397) ^ _month.GetHashCode();\n            hashCode = (hashCode * 397) ^ _dayOfWeek.GetHashCode();\n            hashCode = (hashCode * 397) ^ _nthDayOfWeek.GetHashCode();\n            hashCode = (hashCode * 397) ^ _lastMonthOffset.GetHashCode();\n            hashCode = (hashCode * 397) ^ (int)_flags;\n\n            return hashCode;\n        }\n    }\n\n    /// <summary>\n    /// Implements the operator ==.\n    /// </summary>\n    public static bool operator ==(CronExpression left, CronExpression right) => Equals(left, right);\n\n    /// <summary>\n    /// Implements the operator !=.\n    /// </summary>\n    public static bool operator !=(CronExpression left, CronExpression right) => !Equals(left, right);\n\n    private DateTimeOffset? GetOccurenceByZonedTimes(DateTimeOffset from, TimeZoneInfo zone, bool inclusive)\n    {\n        var fromLocal = from.DateTime;\n\n        if (TimeZoneHelper.IsAmbiguousTime(zone, fromLocal))\n        {\n            var currentOffset = from.Offset;\n            var standardOffset = zone.BaseUtcOffset;\n\n            if (standardOffset != currentOffset)\n            {\n                var daylightOffset = TimeZoneHelper.GetDaylightOffset(zone, fromLocal);\n                var daylightTimeLocalEnd = TimeZoneHelper.GetDaylightTimeEnd(zone, fromLocal, daylightOffset).DateTime;\n\n                // Early period, try to find anything here.\n                var foundInDaylightOffset = FindOccurence(fromLocal.Ticks, daylightTimeLocalEnd.Ticks, inclusive);\n                if (foundInDaylightOffset != NotFound) return new DateTimeOffset(foundInDaylightOffset, daylightOffset);\n\n                fromLocal = TimeZoneHelper.GetStandardTimeStart(zone, fromLocal, daylightOffset).DateTime;\n                inclusive = true;\n            }\n\n            // Skip late ambiguous interval.\n            var ambiguousIntervalLocalEnd = TimeZoneHelper.GetAmbiguousIntervalEnd(zone, fromLocal).DateTime;\n\n            if (HasFlag(CronExpressionFlag.Interval))\n            {\n                var foundInStandardOffset = FindOccurence(fromLocal.Ticks, ambiguousIntervalLocalEnd.Ticks - 1, inclusive);\n                if (foundInStandardOffset != NotFound) return new DateTimeOffset(foundInStandardOffset, standardOffset);\n            }\n\n            fromLocal = ambiguousIntervalLocalEnd;\n            inclusive = true;\n        }\n\n        var occurrenceTicks = FindOccurence(fromLocal.Ticks, inclusive);\n        if (occurrenceTicks == NotFound) return null;\n\n        var occurrence = new DateTime(occurrenceTicks);\n\n        if (zone.IsInvalidTime(occurrence))\n        {\n            var nextValidTime = TimeZoneHelper.GetDaylightTimeStart(zone, occurrence);\n            return nextValidTime;\n        }\n\n        if (TimeZoneHelper.IsAmbiguousTime(zone, occurrence))\n        {\n            var daylightOffset = TimeZoneHelper.GetDaylightOffset(zone, occurrence);\n            return new DateTimeOffset(occurrence, daylightOffset);\n        }\n\n        return new DateTimeOffset(occurrence, zone.GetUtcOffset(occurrence));\n    }\n\n    private long FindOccurence(long startTimeTicks, long endTimeTicks, bool startInclusive)\n    {\n        var found = FindOccurence(startTimeTicks, startInclusive);\n\n        if (found == NotFound || found > endTimeTicks) return NotFound;\n        return found;\n    }\n\n    private long FindOccurence(long ticks, bool startInclusive)\n    {\n        if (!startInclusive) ticks++;\n\n        CalendarHelper.FillDateTimeParts(\n            ticks,\n            out var startSecond,\n            out var startMinute,\n            out var startHour,\n            out var startDay,\n            out var startMonth,\n            out var startYear);\n\n        var minMatchedDay = GetFirstSet(_dayOfMonth);\n\n        var second = startSecond;\n        var minute = startMinute;\n        var hour = startHour;\n        var day = startDay;\n        var month = startMonth;\n        var year = startYear;\n\n        if (!GetBit(_second, second) && !Move(_second, ref second)) minute++;\n        if (!GetBit(_minute, minute) && !Move(_minute, ref minute)) hour++;\n        if (!GetBit(_hour, hour) && !Move(_hour, ref hour)) day++;\n\n        // If NearestWeekday flag is set it's possible forward shift.\n        if (HasFlag(CronExpressionFlag.NearestWeekday)) day = CronField.DaysOfMonth.First;\n\n        if (!GetBit(_dayOfMonth, day) && !Move(_dayOfMonth, ref day)) goto RetryMonth;\n        if (!GetBit(_month, month)) goto RetryMonth;\n\n    Retry:\n\n        if (day > GetLastDayOfMonth(year, month)) goto RetryMonth;\n\n        if (HasFlag(CronExpressionFlag.DayOfMonthLast)) day = GetLastDayOfMonth(year, month);\n\n        var lastCheckedDay = day;\n\n        if (HasFlag(CronExpressionFlag.NearestWeekday)) day = CalendarHelper.MoveToNearestWeekDay(year, month, day);\n\n        if (IsDayOfWeekMatch(year, month, day))\n        {\n            if (CalendarHelper.IsGreaterThan(year, month, day, startYear, startMonth, startDay)) goto RolloverDay;\n            if (hour > startHour) goto RolloverHour;\n            if (minute > startMinute) goto RolloverMinute;\n            goto ReturnResult;\n\n        RolloverDay: hour = GetFirstSet(_hour);\n        RolloverHour: minute = GetFirstSet(_minute);\n        RolloverMinute: second = GetFirstSet(_second);\n\n        ReturnResult:\n\n            var found = CalendarHelper.DateTimeToTicks(year, month, day, hour, minute, second);\n            if (found >= ticks) return found;\n        }\n\n        day = lastCheckedDay;\n        if (Move(_dayOfMonth, ref day)) goto Retry;\n\n    RetryMonth:\n\n        if (!Move(_month, ref month) && ++year >= MaxYear) return NotFound;\n        day = minMatchedDay;\n\n        goto Retry;\n    }\n\n    private static bool Move(long fieldBits, ref int fieldValue)\n    {\n        if (fieldBits >> ++fieldValue == 0)\n        {\n            fieldValue = GetFirstSet(fieldBits);\n            return false;\n        }\n\n        fieldValue += GetFirstSet(fieldBits >> fieldValue);\n        return true;\n    }\n\n    private int GetLastDayOfMonth(int year, int month)\n    {\n        return CalendarHelper.GetDaysInMonth(year, month) - _lastMonthOffset;\n    }\n\n    private bool IsDayOfWeekMatch(int year, int month, int day)\n    {\n        if (HasFlag(CronExpressionFlag.DayOfWeekLast) && !CalendarHelper.IsLastDayOfWeek(year, month, day) ||\n            HasFlag(CronExpressionFlag.NthDayOfWeek) && !CalendarHelper.IsNthDayOfWeek(day, _nthDayOfWeek))\n        {\n            return false;\n        }\n\n        if (_dayOfWeek == CronField.DaysOfWeek.AllBits) return true;\n\n        var dayOfWeek = CalendarHelper.GetDayOfWeek(year, month, day);\n\n        return ((_dayOfWeek >> (int)dayOfWeek) & 1) != 0;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static int GetFirstSet(long value)\n    {\n        var res = unchecked((ulong)(value & -value) * 0x022fdd63cc95386d) >> 58;\n        return DeBruijnPositions[res];\n    }\n\n    private bool HasFlag(CronExpressionFlag value)\n    {\n        return (_flags & value) != 0;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe void SkipWhiteSpaces(ref char* pointer)\n    {\n        while (IsWhiteSpace(*pointer)) { pointer++; }\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe void ParseWhiteSpace(CronField prevField, ref char* pointer)\n    {\n        if (!IsWhiteSpace(*pointer)) ThrowFormatException(prevField, \"Unexpected character '{0}'.\", *pointer);\n        SkipWhiteSpaces(ref pointer);\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe void ParseEndOfString(ref char* pointer)\n    {\n        if (!IsWhiteSpace(*pointer) && !IsEndOfString(*pointer)) ThrowFormatException(CronField.DaysOfWeek, \"Unexpected character '{0}'.\", *pointer);\n\n        SkipWhiteSpaces(ref pointer);\n        if (!IsEndOfString(*pointer)) ThrowFormatException(\"Unexpected character '{0}'.\", *pointer);\n    }\n\n    private static unsafe CronExpression ParseMacro(ref char* pointer)\n    {\n        switch (ToUpper(*pointer++))\n        {\n            case 'A':\n                if (AcceptCharacter(ref pointer, 'N') &&\n                    AcceptCharacter(ref pointer, 'N') &&\n                    AcceptCharacter(ref pointer, 'U') &&\n                    AcceptCharacter(ref pointer, 'A') &&\n                    AcceptCharacter(ref pointer, 'L') &&\n                    AcceptCharacter(ref pointer, 'L') &&\n                    AcceptCharacter(ref pointer, 'Y'))\n                    return Yearly;\n                return null;\n\n            case 'D':\n                if (AcceptCharacter(ref pointer, 'A') &&\n                    AcceptCharacter(ref pointer, 'I') &&\n                    AcceptCharacter(ref pointer, 'L') &&\n                    AcceptCharacter(ref pointer, 'Y'))\n                    return Daily;\n                return null;\n\n            case 'E':\n                if (AcceptCharacter(ref pointer, 'V') &&\n                    AcceptCharacter(ref pointer, 'E') &&\n                    AcceptCharacter(ref pointer, 'R') &&\n                    AcceptCharacter(ref pointer, 'Y') &&\n                    Accept(ref pointer, '_'))\n                {\n                    if (AcceptCharacter(ref pointer, 'M') &&\n                        AcceptCharacter(ref pointer, 'I') &&\n                        AcceptCharacter(ref pointer, 'N') &&\n                        AcceptCharacter(ref pointer, 'U') &&\n                        AcceptCharacter(ref pointer, 'T') &&\n                        AcceptCharacter(ref pointer, 'E'))\n                        return Minutely;\n\n                    if (*(pointer - 1) != '_') return null;\n\n                    if (AcceptCharacter(ref pointer, 'S') &&\n                        AcceptCharacter(ref pointer, 'E') &&\n                        AcceptCharacter(ref pointer, 'C') &&\n                        AcceptCharacter(ref pointer, 'O') &&\n                        AcceptCharacter(ref pointer, 'N') &&\n                        AcceptCharacter(ref pointer, 'D'))\n                        return Secondly;\n                }\n\n                return null;\n\n            case 'H':\n                if (AcceptCharacter(ref pointer, 'O') &&\n                    AcceptCharacter(ref pointer, 'U') &&\n                    AcceptCharacter(ref pointer, 'R') &&\n                    AcceptCharacter(ref pointer, 'L') &&\n                    AcceptCharacter(ref pointer, 'Y'))\n                    return Hourly;\n                return null;\n\n            case 'M':\n                if (AcceptCharacter(ref pointer, 'O') &&\n                    AcceptCharacter(ref pointer, 'N') &&\n                    AcceptCharacter(ref pointer, 'T') &&\n                    AcceptCharacter(ref pointer, 'H') &&\n                    AcceptCharacter(ref pointer, 'L') &&\n                    AcceptCharacter(ref pointer, 'Y'))\n                    return Monthly;\n\n                if (ToUpper(*(pointer - 1)) == 'M' &&\n                    AcceptCharacter(ref pointer, 'I') &&\n                    AcceptCharacter(ref pointer, 'D') &&\n                    AcceptCharacter(ref pointer, 'N') &&\n                    AcceptCharacter(ref pointer, 'I') &&\n                    AcceptCharacter(ref pointer, 'G') &&\n                    AcceptCharacter(ref pointer, 'H') &&\n                    AcceptCharacter(ref pointer, 'T'))\n                    return Daily;\n\n                return null;\n\n            case 'W':\n                if (AcceptCharacter(ref pointer, 'E') &&\n                    AcceptCharacter(ref pointer, 'E') &&\n                    AcceptCharacter(ref pointer, 'K') &&\n                    AcceptCharacter(ref pointer, 'L') &&\n                    AcceptCharacter(ref pointer, 'Y'))\n                    return Weekly;\n                return null;\n\n            case 'Y':\n                if (AcceptCharacter(ref pointer, 'E') &&\n                    AcceptCharacter(ref pointer, 'A') &&\n                    AcceptCharacter(ref pointer, 'R') &&\n                    AcceptCharacter(ref pointer, 'L') &&\n                    AcceptCharacter(ref pointer, 'Y'))\n                    return Yearly;\n                return null;\n\n            default:\n                pointer--;\n                return null;\n        }\n    }\n\n    private static unsafe long ParseField(CronField field, ref char* pointer, ref CronExpressionFlag flags)\n    {\n        if (Accept(ref pointer, '*') || Accept(ref pointer, '?'))\n        {\n            if (field.CanDefineInterval) flags |= CronExpressionFlag.Interval;\n            return ParseStar(field, ref pointer);\n        }\n\n        var num = ParseValue(field, ref pointer);\n\n        var bits = ParseRange(field, ref pointer, num, ref flags);\n        if (Accept(ref pointer, ',')) bits |= ParseList(field, ref pointer, ref flags);\n\n        return bits;\n    }\n\n    private static unsafe long ParseDayOfMonth(ref char* pointer, ref CronExpressionFlag flags, ref byte lastDayOffset)\n    {\n        var field = CronField.DaysOfMonth;\n\n        if (Accept(ref pointer, '*') || Accept(ref pointer, '?')) return ParseStar(field, ref pointer);\n\n        if (AcceptCharacter(ref pointer, 'L')) return ParseLastDayOfMonth(field, ref pointer, ref flags, ref lastDayOffset);\n\n        var dayOfMonth = ParseValue(field, ref pointer);\n\n        if (AcceptCharacter(ref pointer, 'W'))\n        {\n            flags |= CronExpressionFlag.NearestWeekday;\n            return GetBit(dayOfMonth);\n        }\n\n        var bits = ParseRange(field, ref pointer, dayOfMonth, ref flags);\n        if (Accept(ref pointer, ',')) bits |= ParseList(field, ref pointer, ref flags);\n\n        return bits;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe long ParseDayOfWeek(ref char* pointer, ref CronExpressionFlag flags, ref byte nthWeekDay)\n    {\n        var field = CronField.DaysOfWeek;\n        if (Accept(ref pointer, '*') || Accept(ref pointer, '?')) return ParseStar(field, ref pointer);\n\n        var dayOfWeek = ParseValue(field, ref pointer);\n\n        if (AcceptCharacter(ref pointer, 'L')) return ParseLastWeekDay(dayOfWeek, ref flags);\n        if (Accept(ref pointer, '#')) return ParseNthWeekDay(field, ref pointer, dayOfWeek, ref flags, out nthWeekDay);\n\n        var bits = ParseRange(field, ref pointer, dayOfWeek, ref flags);\n        if (Accept(ref pointer, ',')) bits |= ParseList(field, ref pointer, ref flags);\n\n        return bits;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe long ParseStar(CronField field, ref char* pointer)\n    {\n        return Accept(ref pointer, '/')\n            ? ParseStep(field, ref pointer, field.First, field.Last)\n            : field.AllBits;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe long ParseList(CronField field, ref char* pointer, ref CronExpressionFlag flags)\n    {\n        var num = ParseValue(field, ref pointer);\n        var bits = ParseRange(field, ref pointer, num, ref flags);\n\n        do\n        {\n            if (!Accept(ref pointer, ',')) return bits;\n\n            bits |= ParseList(field, ref pointer, ref flags);\n        } while (true);\n    }\n\n    private static unsafe long ParseRange(CronField field, ref char* pointer, int low, ref CronExpressionFlag flags)\n    {\n        if (!Accept(ref pointer, '-'))\n        {\n            if (!Accept(ref pointer, '/')) return GetBit(low);\n\n            if (field.CanDefineInterval) flags |= CronExpressionFlag.Interval;\n            return ParseStep(field, ref pointer, low, field.Last);\n        }\n\n        if (field.CanDefineInterval) flags |= CronExpressionFlag.Interval;\n\n        var high = ParseValue(field, ref pointer);\n        if (Accept(ref pointer, '/')) return ParseStep(field, ref pointer, low, high);\n        return GetBits(field, low, high, 1);\n    }\n\n    private static unsafe long ParseStep(CronField field, ref char* pointer, int low, int high)\n    {\n        // Get the step size -- note: we don't pass the\n        // names here, because the number is not an\n        // element id, it's a step size.  'low' is\n        // sent as a 0 since there is no offset either.\n        var step = ParseNumber(field, ref pointer, 1, field.Last);\n        return GetBits(field, low, high, step);\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe long ParseLastDayOfMonth(CronField field, ref char* pointer, ref CronExpressionFlag flags, ref byte lastMonthOffset)\n    {\n        flags |= CronExpressionFlag.DayOfMonthLast;\n\n        if (Accept(ref pointer, '-')) lastMonthOffset = (byte)ParseNumber(field, ref pointer, 0, field.Last - 1);\n        if (AcceptCharacter(ref pointer, 'W')) flags |= CronExpressionFlag.NearestWeekday;\n        return field.AllBits;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe long ParseNthWeekDay(CronField field, ref char* pointer, int dayOfWeek, ref CronExpressionFlag flags, out byte nthDayOfWeek)\n    {\n        nthDayOfWeek = (byte)ParseNumber(field, ref pointer, MinNthDayOfWeek, MaxNthDayOfWeek);\n        flags |= CronExpressionFlag.NthDayOfWeek;\n        return GetBit(dayOfWeek);\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static long ParseLastWeekDay(int dayOfWeek, ref CronExpressionFlag flags)\n    {\n        flags |= CronExpressionFlag.DayOfWeekLast;\n        return GetBit(dayOfWeek);\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe bool Accept(ref char* pointer, char character)\n    {\n        if (*pointer == character)\n        {\n            pointer++;\n            return true;\n        }\n\n        return false;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe bool AcceptCharacter(ref char* pointer, char character)\n    {\n        if (ToUpper(*pointer) == character)\n        {\n            pointer++;\n            return true;\n        }\n\n        return false;\n    }\n\n    private static unsafe int ParseNumber(CronField field, ref char* pointer, int low, int high)\n    {\n        var num = GetNumber(ref pointer, null);\n        if (num == -1 || num < low || num > high)\n        {\n            ThrowFormatException(field, \"Value must be a number between {0} and {1} (all inclusive).\", low, high);\n        }\n        return num;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static unsafe int ParseValue(CronField field, ref char* pointer)\n    {\n        var num = GetNumber(ref pointer, field.Names);\n        if (num == -1 || num < field.First || num > field.Last)\n        {\n            ThrowFormatException(field, \"Value must be a number between {0} and {1} (all inclusive).\", field.First, field.Last);\n        }\n        return num;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static StringBuilder AppendFieldValue(StringBuilder expressionBuilder, CronField field, long fieldValue)\n    {\n        if (field.AllBits == fieldValue) return expressionBuilder.Append('*');\n\n        // Unset 7 bit for Day of week field because both 0 and 7 stand for Sunday.\n        if (field == CronField.DaysOfWeek) fieldValue &= ~(1 << field.Last);\n\n        for (var i = GetFirstSet(fieldValue); ; i = GetFirstSet(fieldValue >> i << i))\n        {\n            expressionBuilder.Append(i);\n            if (fieldValue >> ++i == 0) break;\n            expressionBuilder.Append(',');\n        }\n\n        return expressionBuilder;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private StringBuilder AppendDayOfMonth(StringBuilder expressionBuilder, int domValue)\n    {\n        if (HasFlag(CronExpressionFlag.DayOfMonthLast))\n        {\n            expressionBuilder.Append('L');\n            if (_lastMonthOffset != 0) expressionBuilder.Append($\"-{_lastMonthOffset}\");\n        }\n        else\n        {\n            AppendFieldValue(expressionBuilder, CronField.DaysOfMonth, (uint)domValue);\n        }\n\n        if (HasFlag(CronExpressionFlag.NearestWeekday)) expressionBuilder.Append('W');\n\n        return expressionBuilder;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private void AppendDayOfWeek(StringBuilder expressionBuilder, int dowValue)\n    {\n        AppendFieldValue(expressionBuilder, CronField.DaysOfWeek, dowValue);\n\n        if (HasFlag(CronExpressionFlag.DayOfWeekLast)) expressionBuilder.Append('L');\n        else if (HasFlag(CronExpressionFlag.NthDayOfWeek)) expressionBuilder.Append($\"#{_nthDayOfWeek}\");\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static long GetBits(CronField field, int num1, int num2, int step)\n    {\n        if (num2 < num1) return GetReversedRangeBits(field, num1, num2, step);\n        if (step == 1) return (1L << (num2 + 1)) - (1L << num1);\n\n        return GetRangeBits(num1, num2, step);\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static long GetRangeBits(int low, int high, int step)\n    {\n        var bits = 0L;\n        for (var i = low; i <= high; i += step)\n        {\n            SetBit(ref bits, i);\n        }\n        return bits;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static long GetReversedRangeBits(CronField field, int num1, int num2, int step)\n    {\n        var high = field.Last;\n        // Skip one of sundays.\n        if (field == CronField.DaysOfWeek) high--;\n\n        var bits = GetRangeBits(num1, high, step);\n\n        num1 = field.First + step - (high - num1) % step - 1;\n        return bits | GetRangeBits(num1, num2, step);\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static long GetBit(int num1)\n    {\n        return 1L << num1;\n    }\n\n    private static unsafe int GetNumber(ref char* pointer, int[] names)\n    {\n        if (IsDigit(*pointer))\n        {\n            var num = GetNumeric(*pointer++);\n\n            if (!IsDigit(*pointer)) return num;\n\n            num = num * 10 + GetNumeric(*pointer++);\n\n            if (!IsDigit(*pointer)) return num;\n            return -1;\n        }\n\n        if (names == null) return -1;\n\n        if (!IsLetter(*pointer)) return -1;\n        var buffer = ToUpper(*pointer++);\n\n        if (!IsLetter(*pointer)) return -1;\n        buffer |= ToUpper(*pointer++) << 8;\n\n        if (!IsLetter(*pointer)) return -1;\n        buffer |= ToUpper(*pointer++) << 16;\n\n        var length = names.Length;\n\n        for (var i = 0; i < length; i++)\n        {\n            if (buffer == names[i])\n            {\n                return i;\n            }\n        }\n\n        return -1;\n    }\n\n    [MethodImpl(MethodImplOptions.NoInlining)]\n    private static void ThrowFormatException(CronField field, string format, params object[] args)\n    {\n        throw new CronFormatException(field, string.Format(format, args));\n    }\n\n    [MethodImpl(MethodImplOptions.NoInlining)]\n    private static void ThrowFormatException(string format, params object[] args)\n    {\n        throw new CronFormatException(string.Format(format, args));\n    }\n\n    [MethodImpl(MethodImplOptions.NoInlining)]\n    private static void ThrowFromShouldBeLessThanToException(string fromName, string toName)\n    {\n        throw new ArgumentException($\"The value of the {fromName} argument should be less than the value of the {toName} argument.\", fromName);\n    }\n\n    [MethodImpl(MethodImplOptions.NoInlining)]\n    private static void ThrowWrongDateTimeKindException(string paramName)\n    {\n        throw new ArgumentException(\"The supplied DateTime must have the Kind property set to Utc\", paramName);\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static bool GetBit(long value, int index)\n    {\n        return (value & (1L << index)) != 0;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static void SetBit(ref long value, int index)\n    {\n        value |= 1L << index;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static bool IsEndOfString(int code)\n    {\n        return code == '\\0';\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static bool IsWhiteSpace(int code)\n    {\n        return code == '\\t' || code == ' ';\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static bool IsDigit(int code)\n    {\n        return code >= 48 && code <= 57;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static bool IsLetter(int code)\n    {\n        return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static int GetNumeric(int code)\n    {\n        return code - 48;\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static int ToUpper(int code)\n    {\n        if (code >= 97 && code <= 122)\n        {\n            return code - 32;\n        }\n\n        return code;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Cron/CronExpressionFlag.cs",
    "content": "﻿// The MIT License(MIT)\n//\n// Copyright (c) 2017 Sergey Odinokov\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nnamespace WeihanLi.Common.Helpers.Cron;\n\n[Flags]\ninternal enum CronExpressionFlag : byte\n{\n    DayOfMonthLast = 0b00001,\n    DayOfWeekLast = 0b00010,\n    Interval = 0b00100,\n    NearestWeekday = 0b01000,\n    NthDayOfWeek = 0b10000\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Cron/CronField.cs",
    "content": "﻿// The MIT License(MIT)\n//\n// Copyright (c) 2017 Sergey Odinokov\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n#nullable disable\n\nnamespace WeihanLi.Common.Helpers.Cron;\n\ninternal sealed class CronField\n{\n    private static readonly string[] MonthNames =\n    [\n        null, \"JAN\", \"FEB\", \"MAR\", \"APR\", \"MAY\", \"JUN\", \"JUL\", \"AUG\", \"SEP\", \"OCT\", \"NOV\", \"DEC\"\n    ];\n\n    private static readonly string[] DayOfWeekNames =\n    [\n        \"SUN\", \"MON\", \"TUE\", \"WED\", \"THU\", \"FRI\", \"SAT\", \"SUN\"\n    ];\n\n    private static readonly int[] MonthNamesArray = new int[MonthNames.Length];\n    private static readonly int[] DayOfWeekNamesArray = new int[DayOfWeekNames.Length];\n\n    // 0 and 7 are both Sunday, for compatibility reasons.\n    public static readonly CronField DaysOfWeek = new(\"Days of week\", 0, 7, DayOfWeekNamesArray, false);\n\n    public static readonly CronField Months = new(\"Months\", 1, 12, MonthNamesArray, false);\n    public static readonly CronField DaysOfMonth = new(\"Days of month\", 1, 31, null, false);\n    public static readonly CronField Hours = new(\"Hours\", 0, 23, null, true);\n    public static readonly CronField Minutes = new(\"Minutes\", 0, 59, null, true);\n    public static readonly CronField Seconds = new(\"Seconds\", 0, 59, null, true);\n\n    static CronField()\n    {\n        for (var i = 1; i < MonthNames.Length; i++)\n        {\n            var name = MonthNames[i].ToUpperInvariant();\n            var array = new char[3];\n            array[0] = name[0];\n            array[1] = name[1];\n            array[2] = name[2];\n\n            var combined = name[0] | (name[1] << 8) | (name[2] << 16);\n\n            MonthNamesArray[i] = combined;\n        }\n\n        for (var i = 0; i < DayOfWeekNames.Length; i++)\n        {\n            var name = DayOfWeekNames[i].ToUpperInvariant();\n            var array = new char[3];\n            array[0] = name[0];\n            array[1] = name[1];\n            array[2] = name[2];\n\n            var combined = name[0] | (name[1] << 8) | (name[2] << 16);\n\n            DayOfWeekNamesArray[i] = combined;\n        }\n    }\n\n    public readonly string Name;\n    public readonly int First;\n    public readonly int Last;\n    public readonly int[] Names;\n    public readonly bool CanDefineInterval;\n    public readonly long AllBits;\n\n    private CronField(string name, int first, int last, int[] names, bool canDefineInterval)\n    {\n        Name = name;\n        First = first;\n        Last = last;\n        Names = names;\n        CanDefineInterval = canDefineInterval;\n        for (var i = First; i <= Last; i++)\n        {\n            AllBits |= (1L << i);\n        }\n    }\n\n    public override string ToString()\n    {\n        return Name;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Cron/CronFormat.cs",
    "content": "﻿// The MIT License(MIT)\n//\n// Copyright (c) 2017 Sergey Odinokov\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nnamespace WeihanLi.Common.Helpers.Cron;\n\n/// <summary>\n/// Defines the cron format options that customize string parsing for <see cref=\"CronExpression.Parse(string, CronFormat)\"/>.\n/// </summary>\npublic enum CronFormat : byte\n{\n    /// <summary>\n    /// Parsing string must contain only 5 fields: minute, hour, day of month, month, day of week.\n    /// </summary>\n    Standard = 0,\n\n    /// <summary>\n    /// Second field must be specified in parsing string.\n    /// </summary>\n    IncludeSeconds = 1\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Cron/CronFormatException.cs",
    "content": "﻿// The MIT License(MIT)\n//\n// Copyright (c) 2017 Sergey Odinokov\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nnamespace WeihanLi.Common.Helpers.Cron;\n\n/// <summary>\n/// Represents an exception that's thrown, when invalid Cron expression is given.\n/// </summary>\n/// <remarks>\n/// Initializes a new instance of the <see cref=\"CronFormatException\"/> class with\n/// the given message.\n/// </remarks>\n[Serializable]\ninternal class CronFormatException(string message) : FormatException(message)\n{\n    internal CronFormatException(CronField field, string message) : this($\"{field}: {message}\")\n    {\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Cron/TimeZoneHelper.cs",
    "content": "﻿// The MIT License(MIT)\n//\n// Copyright (c) 2017 Sergey Odinokov\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nnamespace WeihanLi.Common.Helpers.Cron;\n\ninternal static class TimeZoneHelper\n{\n    // This method is here because .NET Framework, .NET Core and Mono works different when transition from standard time (ST) to\n    // daylight saving time (DST) happens.\n    // When DST ends you set the clocks backward. If you are in USA it happens on first sunday of November at 2:00 am.\n    // So duration from 1:00 am to 2:00 am repeats twice.\n    // .NET Framework and .NET Core consider backward DST transition as [1:00 DST ->> 2:00 DST)--[1:00 ST --> 2:00 ST]. So 2:00 is not ambiguous, but 1:00 is ambiguous.\n    // Mono consider backward DST transition as [1:00 DST ->> 2:00 DST]--(1:00 ST --> 2:00 ST]. So 2:00 is ambiguous, but 1:00 is not ambiguous.\n    // We have to add 1 tick to ambiguousTime to have the same behavior for all frameworks. Thus 1:00 is ambiguous and 2:00 is not ambiguous.\n    public static bool IsAmbiguousTime(TimeZoneInfo zone, DateTime ambiguousTime)\n    {\n        return zone.IsAmbiguousTime(ambiguousTime.AddTicks(1));\n    }\n\n    public static TimeSpan GetDaylightOffset(TimeZoneInfo zone, DateTime ambiguousDateTime)\n    {\n        var offsets = GetAmbiguousOffsets(zone, ambiguousDateTime);\n        var baseOffset = zone.BaseUtcOffset;\n\n        if (offsets[0] != baseOffset) return offsets[0];\n\n        return offsets[1];\n    }\n\n    public static DateTimeOffset GetDaylightTimeStart(TimeZoneInfo zone, DateTime invalidDateTime)\n    {\n        var dstTransitionDateTime = new DateTime(invalidDateTime.Year, invalidDateTime.Month, invalidDateTime.Day,\n            invalidDateTime.Hour, invalidDateTime.Minute, 0, 0, invalidDateTime.Kind);\n\n        while (zone.IsInvalidTime(dstTransitionDateTime))\n        {\n            dstTransitionDateTime = dstTransitionDateTime.AddMinutes(1);\n        }\n\n        var dstOffset = zone.GetUtcOffset(dstTransitionDateTime);\n\n        return new DateTimeOffset(dstTransitionDateTime, dstOffset);\n    }\n\n    public static DateTimeOffset GetStandardTimeStart(TimeZoneInfo zone, DateTime ambiguousTime, TimeSpan daylightOffset)\n    {\n        var dstTransitionEnd = GetDstTransitionEndDateTime(zone, ambiguousTime);\n\n        return new DateTimeOffset(dstTransitionEnd, daylightOffset).ToOffset(zone.BaseUtcOffset);\n    }\n\n    public static DateTimeOffset GetAmbiguousIntervalEnd(TimeZoneInfo zone, DateTime ambiguousTime)\n    {\n        var dstTransitionEnd = GetDstTransitionEndDateTime(zone, ambiguousTime);\n\n        return new DateTimeOffset(dstTransitionEnd, zone.BaseUtcOffset);\n    }\n\n    public static DateTimeOffset GetDaylightTimeEnd(TimeZoneInfo zone, DateTime ambiguousTime, TimeSpan daylightOffset)\n    {\n        var daylightTransitionEnd = GetDstTransitionEndDateTime(zone, ambiguousTime);\n\n        return new DateTimeOffset(daylightTransitionEnd.AddTicks(-1), daylightOffset);\n    }\n\n    private static TimeSpan[] GetAmbiguousOffsets(TimeZoneInfo zone, DateTime ambiguousTime)\n    {\n        return zone.GetAmbiguousTimeOffsets(ambiguousTime.AddTicks(1));\n    }\n\n    private static DateTime GetDstTransitionEndDateTime(TimeZoneInfo zone, DateTime ambiguousDateTime)\n    {\n        var dstTransitionDateTime = new DateTime(ambiguousDateTime.Year, ambiguousDateTime.Month, ambiguousDateTime.Day,\n            ambiguousDateTime.Hour, ambiguousDateTime.Minute, 0, 0, ambiguousDateTime.Kind);\n\n        while (zone.IsAmbiguousTime(dstTransitionDateTime))\n        {\n            dstTransitionDateTime = dstTransitionDateTime.AddMinutes(1);\n        }\n\n        return dstTransitionDateTime;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/CronHelper.cs",
    "content": "﻿using WeihanLi.Common.Helpers.Cron;\n\nnamespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// CronExpression Helper\n/// Cron Expression : http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html\n/// </summary>\npublic static class CronHelper\n{\n    public static readonly string Yearly = CronExpression.Yearly.ToString();\n    public static readonly string Weekly = CronExpression.Weekly.ToString();\n    public static readonly string Monthly = CronExpression.Monthly.ToString();\n    public static readonly string Daily = CronExpression.Daily.ToString();\n    public static readonly string Hourly = CronExpression.Hourly.ToString();\n    public static readonly string Minutely = CronExpression.Minutely.ToString();\n    public static readonly string Secondly = CronExpression.Secondly.ToString();\n\n    /// <summary>\n    /// GetNextOccurrence\n    /// </summary>\n    /// <param name=\"cron\">cron</param>\n    /// <returns>next occurrence time</returns>\n    public static DateTimeOffset? GetNextOccurrence(string cron)\n    {\n        var expression = CronExpression.Parse(cron);\n        return expression.GetNextOccurrence(DateTimeOffset.UtcNow, TimeZoneInfo.Utc);\n    }\n\n    /// <summary>\n    /// GetNextOccurrence\n    /// </summary>\n    /// <param name=\"cron\">cron</param>\n    /// <param name=\"timeZoneInfo\">timeZoneInfo</param>\n    /// <returns>next occurrence time</returns>\n    public static DateTimeOffset? GetNextOccurrence(string cron, TimeZoneInfo timeZoneInfo)\n    {\n        var expression = CronExpression.Parse(cron);\n        return expression.GetNextOccurrence(DateTimeOffset.UtcNow, timeZoneInfo);\n    }\n\n    /// <summary>\n    /// GetNextOccurrence\n    /// </summary>\n    /// <param name=\"cron\">cron expression</param>\n    /// <param name=\"period\">next period</param>\n    /// <returns>next occurrence times</returns>\n    public static IEnumerable<DateTimeOffset> GetNextOccurrences(string cron, TimeSpan period)\n    {\n        var expression = CronExpression.Parse(cron);\n        var fromUtc = DateTime.UtcNow;\n        return expression.GetOccurrences(fromUtc, fromUtc.Add(period), TimeZoneInfo.Utc);\n    }\n\n    /// <summary>\n    /// GetNextOccurrence\n    /// </summary>\n    /// <param name=\"cron\">cron expression</param>\n    /// <param name=\"period\">next period</param>\n    /// <param name=\"timeZoneInfo\">timeZoneInfo</param>\n    /// <returns>next occurrence times</returns>\n    public static IEnumerable<DateTimeOffset> GetNextOccurrences(string cron, TimeSpan period, TimeZoneInfo timeZoneInfo)\n    {\n        var expression = CronExpression.Parse(cron);\n        var fromUtc = DateTime.UtcNow;\n        return expression.GetOccurrences(fromUtc, fromUtc.Add(period), timeZoneInfo);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/DataSerializer.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Xml.Serialization;\nusing WeihanLi.Common.Compressor;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic interface IDataSerializer\n{\n    /// <summary>\n    /// 序列化\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"obj\">object</param>\n    /// <returns>bytes</returns>\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    byte[] Serialize<T>(T obj);\n\n    /// <summary>\n    /// 反序列化\n    /// </summary>\n    /// <typeparam name=\"T\">Type</typeparam>\n    /// <param name=\"bytes\">bytes</param>\n    /// <returns>obj</returns>\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    T Deserialize<T>(byte[] bytes);\n}\n\npublic class XmlDataSerializer : IDataSerializer\n{\n    internal static readonly Lazy<XmlDataSerializer> Instance = new();\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public virtual byte[] Serialize<T>(T obj)\n    {\n        if (typeof(Task).IsAssignableFrom(typeof(T)))\n        {\n            throw new ArgumentException(Resource.TaskCanNotBeSerialized);\n        }\n        Guard.NotNull(obj);\n        using var ms = new MemoryStream();\n        var serializer = new XmlSerializer(typeof(T));\n        serializer.Serialize(ms, obj);\n        return ms.ToArray();\n    }\n\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    public virtual T Deserialize<T>(byte[] bytes)\n    {\n        if (typeof(Task).IsAssignableFrom(typeof(T)))\n        {\n            throw new ArgumentException(Resource.TaskCanNotBeSerialized);\n        }\n        Guard.NotNull(bytes);\n        using var ms = new MemoryStream(bytes);\n        var serializer = new XmlSerializer(typeof(T));\n        return (T)serializer.Deserialize(ms)!;\n    }\n\n}\n\npublic class JsonDataSerializer : IDataSerializer\n{\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public virtual byte[] Serialize<T>(T obj)\n    {\n        if (typeof(Task).IsAssignableFrom(typeof(T)))\n        {\n            throw new ArgumentException(Resource.TaskCanNotBeSerialized);\n        }\n        Guard.NotNull(obj);\n        return obj.ToJson().GetBytes();\n    }\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public virtual T Deserialize<T>(byte[] bytes)\n    {\n        if (typeof(Task).IsAssignableFrom(typeof(T)))\n        {\n            throw new ArgumentException(Resource.TaskCanNotBeSerialized);\n        }\n        Guard.NotNull(bytes);\n        return bytes.GetString().JsonToObject<T>();\n    }\n}\n\npublic sealed class CompressDataSerializer(IDataSerializer serializer, IDataCompressor compressor) : IDataSerializer\n{\n    private readonly IDataSerializer _serializer = Guard.NotNull(serializer);\n    private readonly IDataCompressor _compressor = Guard.NotNull(compressor);\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public byte[] Serialize<T>(T obj)\n    {\n        Guard.NotNull(obj);\n        return _compressor.Compress(_serializer.Serialize(obj));\n    }\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public T Deserialize<T>(byte[] bytes)\n    {\n        Guard.NotNull(bytes);\n        return _serializer.Deserialize<T>(_compressor.Decompress(bytes));\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/DelegateHelper.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class DelegateHelper\n{\n    private static readonly Type[] _funcFactory;\n    private static readonly Type[] _actionFactory;\n\n    static DelegateHelper()\n    {\n        _funcFactory = new Type[18];\n        _funcFactory[0] = typeof(Func<>);\n        _funcFactory[1] = typeof(Func<,>);\n        _funcFactory[2] = typeof(Func<,,>);\n        _funcFactory[3] = typeof(Func<,,,>);\n        _funcFactory[4] = typeof(Func<,,,,>);\n        _funcFactory[5] = typeof(Func<,,,,,>);\n        _funcFactory[6] = typeof(Func<,,,,,,>);\n        _funcFactory[7] = typeof(Func<,,,,,,,>);\n        _funcFactory[8] = typeof(Func<,,,,,,,,>);\n        _funcFactory[9] = typeof(Func<,,,,,,,,,>);\n        _funcFactory[10] = typeof(Func<,,,,,,,,,,>);\n        _funcFactory[11] = typeof(Func<,,,,,,,,,,,>);\n        _funcFactory[12] = typeof(Func<,,,,,,,,,,,,>);\n        _funcFactory[13] = typeof(Func<,,,,,,,,,,,,,>);\n        _funcFactory[14] = typeof(Func<,,,,,,,,,,,,,,>);\n        _funcFactory[15] = typeof(Func<,,,,,,,,,,,,,,,>);\n        _funcFactory[16] = typeof(Func<,,,,,,,,,,,,,,,>);\n        _funcFactory[17] = typeof(Func<,,,,,,,,,,,,,,,,>);\n\n        _actionFactory = new Type[17];\n        _actionFactory[0] = typeof(Action);\n        _actionFactory[1] = typeof(Action<>);\n        _actionFactory[2] = typeof(Action<,>);\n        _actionFactory[3] = typeof(Action<,,>);\n        _actionFactory[4] = typeof(Action<,,,>);\n        _actionFactory[5] = typeof(Action<,,,,>);\n        _actionFactory[6] = typeof(Action<,,,,,>);\n        _actionFactory[7] = typeof(Action<,,,,,,>);\n        _actionFactory[8] = typeof(Action<,,,,,,,>);\n        _actionFactory[9] = typeof(Action<,,,,,,,,>);\n        _actionFactory[10] = typeof(Action<,,,,,,,,,>);\n        _actionFactory[11] = typeof(Action<,,,,,,,,,,>);\n        _actionFactory[12] = typeof(Action<,,,,,,,,,,,>);\n        _actionFactory[13] = typeof(Action<,,,,,,,,,,,,>);\n        _actionFactory[14] = typeof(Action<,,,,,,,,,,,,,>);\n        _actionFactory[15] = typeof(Action<,,,,,,,,,,,,,,>);\n        _actionFactory[16] = typeof(Action<,,,,,,,,,,,,,,,>);\n    }\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    public static Delegate FromMethod(MethodInfo method, object? target = null)\n    {\n        Guard.NotNull(method);\n        var delegateType = GetDelegateType(method);\n        return method.CreateDelegate(delegateType, target);\n    }\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    public static Type GetDelegateType(MethodInfo method)\n    {\n        Guard.NotNull(method);\n        var isVoidMethod = method.ReturnType == typeof(void);\n        var parameterTypes = method.GetParameters()\n            .Select(p => p.ParameterType)\n            .ToArray();\n\n        Type delegateType;\n        if (isVoidMethod)\n        {\n            if (parameterTypes.Length == 0)\n            {\n                delegateType = _actionFactory[0];\n            }\n            else\n            {\n                delegateType = _actionFactory[parameterTypes.Length]\n                    .MakeGenericType(parameterTypes);\n            }\n        }\n        else\n        {\n            var types = new Type[parameterTypes.Length + 1];\n            Array.Copy(parameterTypes, types, parameterTypes.Length);\n            types[parameterTypes.Length] = method.ReturnType;\n\n            delegateType = _funcFactory[parameterTypes.Length]\n                .MakeGenericType(types);\n        }\n\n        return delegateType;\n    }\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    public static Type GetDelegate(Type[]? parametersTypes = null, Type? returnType = null)\n    {\n        if (returnType == null || returnType == typeof(void))\n        {\n            return GetAction(parametersTypes);\n        }\n\n        return GetFunc(returnType, parametersTypes);\n    }\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    public static Type GetFunc(Type returnType, params Type[]? parametersTypes)\n    {\n        if (returnType == typeof(void))\n        {\n            return GetAction(parametersTypes);\n        }\n\n        if (parametersTypes.IsNullOrEmpty())\n        {\n            return _funcFactory[0].MakeGenericType(returnType);\n        }\n\n        var types = new Type[parametersTypes.Length + 1];\n        Array.Copy(parametersTypes, types, parametersTypes.Length);\n        types[parametersTypes.Length] = returnType;\n\n        return _funcFactory[parametersTypes.Length]\n            .MakeGenericType(types);\n    }\n\n    [RequiresDynamicCode(\"The native code for this instantiation might not be available at runtime.\")]\n    [RequiresUnreferencedCode(\"If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.\")]\n    public static Type GetAction(params Type[]? parametersTypes)\n    {\n        if (parametersTypes == null || parametersTypes.Length == 0)\n        {\n            return _actionFactory[0];\n        }\n\n        return _actionFactory[parametersTypes.Length]\n            .MakeGenericType(parametersTypes);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/DelegateTextWriter.cs",
    "content": "﻿using System.Text;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic sealed class DelegateTextWriter(Action<string> onLineWritten) : TextWriter\n{\n    public override Encoding Encoding => Encoding.UTF8;\n\n    private readonly Action<string> _onLineWritten = Guard.NotNull(onLineWritten);\n    private readonly StringBuilder _builder = new();\n\n    public override void Flush()\n    {\n        if (_builder.Length > 0)\n        {\n            FlushInternal();\n        }\n    }\n\n    public override Task FlushAsync()\n    {\n        Flush();\n        return Task.CompletedTask;\n    }\n\n    public override void Write(char value)\n    {\n        if (value == '\\n')\n        {\n            FlushInternal();\n        }\n        else\n        {\n            _builder.Append(value);\n        }\n    }\n\n    public override Task WriteAsync(char value)\n    {\n        if (value == '\\n')\n        {\n            FlushInternal();\n        }\n        else\n        {\n            _builder.Append(value);\n        }\n        return Task.CompletedTask;\n    }\n\n    private void FlushInternal()\n    {\n        _onLineWritten(_builder.ToString());\n        _builder.Clear();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/DiagnosticHelper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Diagnostics;\nusing System.Diagnostics.Metrics;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class DiagnosticHelper\n{\n    private const string DiagnosticSourceName = \"WeihanLi.Common\";\n\n    [CLSCompliant(false)]\n    public static readonly ActivitySource ActivitySource;\n    [CLSCompliant(false)]\n    public static readonly Meter Meter;\n\n    static DiagnosticHelper()\n    {\n        var version = typeof(DiagnosticHelper).Assembly.GetName().Version?.ToString(3);\n        ActivitySource = new(DiagnosticSourceName, version);\n        Meter = new(DiagnosticSourceName, version);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/DisposableHelper.cs",
    "content": "﻿namespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// A singleton disposable that does nothing when disposed.\n/// </summary>\npublic sealed class NullDisposable : IDisposable, IAsyncDisposable\n{\n    private NullDisposable()\n    {\n    }\n\n    public void Dispose()\n    {\n    }\n\n    public ValueTask DisposeAsync() =>\n#if NET\n        ValueTask.CompletedTask\n#else\n        default\n#endif\n    ;\n\n    /// <summary>\n    /// Gets the instance of <see cref=\"NullDisposable\"/>.\n    /// </summary>\n    public static NullDisposable Instance { get; } = new();\n}\n\n/// <summary>\n/// Delegate-based Disposable implement\n/// </summary>\n/// <param name=\"disposeAction\">dispose delegate</param>\npublic sealed class DisposableAction(Action? disposeAction) : IDisposable, IAsyncDisposable\n{\n    private Action? _disposeAction = disposeAction;\n\n    public void Dispose()\n    {\n        Interlocked.Exchange(ref _disposeAction, null)?.Invoke();\n    }\n\n    public ValueTask DisposeAsync()\n    {\n        Dispose();\n        return\n#if NET\n            ValueTask.CompletedTask\n#else\n            default\n#endif\n            ;\n    }\n}\n\n/// <summary>\n/// Disposable base class\n/// https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose\n/// https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync\n/// https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync#implement-both-dispose-and-async-dispose-patterns\n/// </summary>\npublic abstract class DisposableBase : IDisposable, IAsyncDisposable\n{\n    // To detect redundant calls\n    private int _disposedStatus;\n\n    public void Dispose()\n    {\n        if (Interlocked.CompareExchange(ref _disposedStatus, 1, 0) != 0) return;\n\n        Dispose(disposing: true);\n        GC.SuppressFinalize(this);\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        if (Interlocked.CompareExchange(ref _disposedStatus, 1, 0) != 0) return;\n\n        // Perform async cleanup.\n        await DisposeAsyncCore().ConfigureAwait(false);\n\n        // managed resources disposed in Dispose()\n        // unmanaged resources dispose\n        Dispose();\n\n        // Suppress finalization\n        GC.SuppressFinalize(this);\n    }\n\n    protected virtual void Dispose(bool disposing)\n    {\n        if (disposing)\n        {\n            // dispose managed state (managed objects)\n        }\n\n        // free unmanaged resources (unmanaged objects) and override finalizer\n        // set large fields to null\n    }\n\n    protected virtual ValueTask DisposeAsyncCore()\n    {\n        // dispose managed state in async way (managed objects)\n        return\n#if NET\n            ValueTask.CompletedTask\n#else\n            default\n#endif\n            ;\n    }\n\n    ~DisposableBase()\n    {\n        // dispose unmanaged resources\n        Dispose(false);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Encoder.cs",
    "content": "﻿using System.Numerics;\nusing System.Text;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class Base62Encoder\n{\n    private const string Charset = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\n    public static string Encode(Guid guid)\n    {\n        var bytes = guid.ToByteArray().ToList();\n\n        if (BitConverter.IsLittleEndian)\n            bytes.Add(0);\n        else\n            bytes.Insert(0, 0);\n\n        return Encode(bytes.ToArray());\n    }\n\n    public static string Encode(string text)\n    {\n        var bytes = Encoding.ASCII.GetBytes(text);\n\n        if (BitConverter.IsLittleEndian)\n            Array.Reverse(bytes);\n\n        return Encode(bytes);\n    }\n\n    public static string Encode(long num)\n    {\n        var bytes = BitConverter.GetBytes(num).ToList();\n\n        if (BitConverter.IsLittleEndian)\n            bytes.Add(0);\n        else\n            bytes.Insert(0, 0);\n\n        return Encode(bytes.ToArray());\n    }\n\n    private static string Encode(byte[] bytes)\n    {\n        var result = string.Empty;\n\n        var bi = new BigInteger(bytes);\n\n        do\n        {\n            result = Charset[(int)(bi % 62)] + result;\n            bi /= 62;\n        } while (bi > 0);\n\n        return result;\n    }\n\n    public static string DecodeString(string codeStr)\n    {\n        var bytes = Decode(codeStr);\n        if (BitConverter.IsLittleEndian)\n        {\n            Array.Reverse(bytes);\n        }\n        return Encoding.ASCII.GetString(bytes);\n    }\n\n    public static Guid DecodeGuid(string codeStr)\n    {\n        var bytes = Decode(codeStr).ToList();\n\n        if (bytes.Count > 16)\n        {\n            if (BitConverter.IsLittleEndian)\n            {\n                bytes.RemoveAt(bytes.Count - 1);\n            }\n            else\n            {\n                bytes.RemoveAt(0);\n            }\n        }\n        else if (bytes.Count < 16)\n        {\n            bytes.AddRange(Enumerable.Repeat((byte)0, 16 - bytes.Count));\n        }\n\n        return new Guid(bytes.ToArray());\n    }\n\n    public static long DecodeLong(string codeStr)\n    {\n        var bytes = Decode(codeStr).ToList();\n        if (bytes.Count > 8)\n        {\n            if (BitConverter.IsLittleEndian)\n            {\n                bytes.RemoveAt(bytes.Count - 1);\n            }\n            else\n            {\n                bytes.RemoveAt(0);\n            }\n        }\n        else if (bytes.Count < 8)\n        {\n            bytes.AddRange(Enumerable.Repeat((byte)0, 8 - bytes.Count));\n        }\n        return BitConverter.ToInt64([.. bytes], 0);\n    }\n\n    private static byte[] Decode(string codedStr)\n    {\n        var result = new BigInteger(0);\n        var len = codedStr.Length;\n\n        for (var i = 0; i < len; i++)\n        {\n            var ch = codedStr[i];\n            var num = Charset.IndexOf(ch);\n            result += num * BigInteger.Pow(62, len - i - 1);\n        }\n\n        return result.ToByteArray();\n    }\n}\n\npublic static class Base36Encoder\n{\n    private const string Charset = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n\n    public static string Encode(Guid guid)\n    {\n        var bytes = guid.ToByteArray().ToList();\n\n        if (BitConverter.IsLittleEndian)\n            bytes.Add(0);\n        else\n            bytes.Insert(0, 0);\n\n        return Encode(bytes.ToArray());\n    }\n\n    public static string Encode(string text)\n    {\n        if (string.IsNullOrEmpty(text))\n            return string.Empty;\n\n        var bytes = Encoding.ASCII.GetBytes(text.ToUpper());\n\n        if (BitConverter.IsLittleEndian)\n            Array.Reverse(bytes);\n\n        return Encode(bytes);\n    }\n\n    public static string Encode(long num)\n    {\n        var bytes = BitConverter.GetBytes(num).ToList();\n\n        if (BitConverter.IsLittleEndian)\n            bytes.Add(0);\n        else\n            bytes.Insert(0, 0);\n\n        return Encode(bytes.ToArray());\n    }\n\n    private static string Encode(byte[] bytes)\n    {\n        var result = string.Empty;\n\n        var bi = new BigInteger(bytes);\n        do\n        {\n            result = Charset[(int)(bi % 36)] + result;\n            bi /= 36;\n        } while (bi > 0);\n\n        return result;\n    }\n\n    public static string DecodeString(string codeStr)\n    {\n        if (string.IsNullOrEmpty(codeStr))\n            return string.Empty;\n\n        var bytes = Decode(codeStr);\n        if (BitConverter.IsLittleEndian)\n        {\n            Array.Reverse(bytes);\n        }\n        return Encoding.ASCII.GetString(bytes);\n    }\n\n    public static Guid DecodeGuid(string codeStr)\n    {\n        var bytes = Decode(codeStr).ToList();\n\n        if (bytes.Count > 16)\n        {\n            if (BitConverter.IsLittleEndian)\n            {\n                bytes.RemoveAt(bytes.Count - 1);\n            }\n            else\n            {\n                bytes.RemoveAt(0);\n            }\n        }\n        else if (bytes.Count < 16)\n        {\n            bytes.AddRange(Enumerable.Repeat((byte)0, 16 - bytes.Count));\n        }\n\n        return new Guid(bytes.ToArray());\n    }\n\n    public static long DecodeLong(string codeStr)\n    {\n        var bytes = Decode(codeStr).ToList();\n        if (bytes.Count > 8)\n        {\n            if (BitConverter.IsLittleEndian)\n            {\n                bytes.RemoveAt(bytes.Count - 1);\n            }\n            else\n            {\n                bytes.RemoveAt(0);\n            }\n        }\n        else if (bytes.Count < 8)\n        {\n            bytes.AddRange(Enumerable.Repeat((byte)0, 8 - bytes.Count));\n        }\n        return BitConverter.ToInt64([.. bytes], 0);\n    }\n\n    private static byte[] Decode(string codedStr)\n    {\n        if (string.IsNullOrEmpty(codedStr))\n        {\n            return [];\n        }\n\n        var result = new BigInteger(0);\n        var len = codedStr.Length;\n\n        for (var i = 0; i < len; i++)\n        {\n            var ch = codedStr[i];\n            var num = Charset.IndexOf(ch);\n            result += num * BigInteger.Pow(36, len - i - 1);\n        }\n\n        var bytes = result.ToByteArray();\n\n        return bytes;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Enricher.cs",
    "content": "﻿namespace WeihanLi.Common.Helpers;\n\npublic interface IEnricher<in TContext>\n{\n    void Enrich(TContext context);\n}\n\npublic abstract class PropertyEnricher<TContext> : IEnricher<TContext>\n{\n    private readonly string _propertyName;\n    private readonly Func<TContext, object> _propertyValueFactory;\n    private readonly bool _overwrite;\n    private readonly Func<TContext, bool>? _propertyPredict;\n\n    /// <summary>\n    /// EnrichAction\n    /// (context,propertyName,propertyValueFactory,overwrite)=>{}\n    /// </summary>\n    protected abstract Action<TContext, string, Func<TContext, object>, bool>? EnrichAction\n    {\n        get;\n    }\n\n    protected PropertyEnricher(string propertyName, object propertyValue, bool overwrite = false) : this(propertyName, _ => propertyValue, overwrite)\n    {\n    }\n\n    protected PropertyEnricher(string propertyName, Func<TContext, object> propertyValueFactory,\n        bool overwrite = false) : this(propertyName, propertyValueFactory, null, overwrite)\n    {\n    }\n\n    protected PropertyEnricher(string propertyName, Func<TContext, object> propertyValueFactory, Func<TContext, bool>? propertyPredict,\n        bool overwrite = false)\n    {\n        _propertyName = propertyName;\n        _propertyValueFactory = propertyValueFactory;\n        _propertyPredict = propertyPredict;\n        _overwrite = overwrite;\n    }\n\n    public void Enrich(TContext context)\n    {\n        if (EnrichAction != null && _propertyPredict?.Invoke(context) != false)\n        {\n            EnrichAction.Invoke(context, _propertyName, _propertyValueFactory, _overwrite);\n        }\n    }\n}\n\npublic interface IAsyncEnricher<in TContext>\n{\n    Task Enrich(TContext context);\n}\n\npublic abstract class AsyncPropertyEnricher<TContext> : IAsyncEnricher<TContext>\n{\n    private readonly string _propertyName;\n    private readonly Func<TContext, object>? _propertyValueFactory;\n    private readonly bool _overwrite;\n    private readonly Func<TContext, bool>? _propertyPredict;\n\n    protected abstract Func<TContext, string, Func<TContext, object>?, bool, Task>? EnrichAction\n    {\n        get;\n    }\n\n    protected AsyncPropertyEnricher(string propertyName, object propertyValue, bool overwrite = false) : this(propertyName, _ => propertyValue, overwrite)\n    {\n    }\n\n    protected AsyncPropertyEnricher(string propertyName, Func<TContext, object>? propertyValueFactory,\n        bool overwrite = false) : this(propertyName, propertyValueFactory, null, overwrite)\n    {\n    }\n\n    protected AsyncPropertyEnricher(string propertyName, Func<TContext, object>? propertyValueFactory, Func<TContext, bool>? propertyPredict,\n        bool overwrite = false)\n    {\n        _propertyName = propertyName;\n        _propertyValueFactory = propertyValueFactory;\n        _propertyPredict = propertyPredict;\n        _overwrite = overwrite;\n    }\n\n    public async Task Enrich(TContext context)\n    {\n        if (EnrichAction != null && _propertyPredict?.Invoke(context) != false)\n        {\n            await EnrichAction.Invoke(context, _propertyName, _propertyValueFactory, _overwrite);\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/EnumHelper.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Common.Models;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class EnumHelper\n{\n    public static IReadOnlyList<IdNameModel> ToIdNameList<TEnum>() where TEnum : Enum\n    {\n        var enumType = typeof(TEnum);\n        return Array.ConvertAll(Enum.GetNames(enumType), name => new IdNameModel()\n        {\n            Name = name,\n            Id = Convert.ToInt32(Enum.Parse(enumType, name))\n        });\n    }\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static IReadOnlyList<IdNameModel<TValue>> ToIdNameList<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum, TValue>() where TEnum : Enum\n    {\n        var enumType = typeof(TEnum);\n        return Array.ConvertAll(Enum.GetNames(enumType), name => new IdNameModel<TValue>()\n        {\n            Id = Enum.Parse(enumType, name).To<TValue>(),\n            Name = name,\n        });\n    }\n\n    public static IReadOnlyList<IdNameDescModel> ToIdNameDescList<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum>() where TEnum : Enum\n    {\n        var enumType = typeof(TEnum);\n        return Array.ConvertAll(Enum.GetNames(enumType), converter: name => new IdNameDescModel()\n        {\n            Name = name,\n            Id = Convert.ToInt32(Enum.Parse(enumType, name)),\n            Description = enumType.GetField(name)?.GetDescription()\n        });\n    }\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static IReadOnlyList<IdNameDescModel<TValue>> ToIdNameDescList<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum, TValue>() where TEnum : Enum\n    {\n        var enumType = typeof(TEnum);\n        return Array.ConvertAll(Enum.GetNames(enumType), converter: name => new IdNameDescModel<TValue>()\n        {\n            Id = Enum.Parse(enumType, name).To<TValue>(),\n            Name = name,\n            Description = enumType.GetField(name)?.GetDescription()\n        });\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/EnvHelper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class EnvHelper\n{\n    [return: NotNullIfNotNull(nameof(defaultValue))]\n    public static string? Val(string envName, string? defaultValue = null)\n    {\n        return Environment.GetEnvironmentVariable(envName) ?? defaultValue;\n    }\n\n    public static bool BooleanVal(string envName, bool defaultValue = false)\n    {\n        var val = Environment.GetEnvironmentVariable(envName);\n        return val.ToBoolean(defaultValue);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ExpressionHelper.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class ExpressionHelper\n{\n    public static Expression<Func<T, bool>> True<T>()\n    {\n        return ConstantExpressions<T>.TrueExpression;\n    }\n\n    public static Expression<Func<T, bool>> False<T>()\n    {\n        return ConstantExpressions<T>.FalseExpression;\n    }\n\n\n    [RequiresUnreferencedCode(\"Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.\")]\n    public static Expression<Func<T, TProperty>> GetPropertySelector<T, TProperty>(string propertyName)\n    {\n        var arg = Expression.Parameter(typeof(T), \"x\");\n        var property = Expression.Property(arg, propertyName);\n        var exp = Expression.Lambda<Func<T, TProperty>>(property, [arg]);\n        return exp;\n    }\n\n    private static class ConstantExpressions<T>\n    {\n        public static readonly Expression<Func<T, bool>> TrueExpression = t => true;\n\n        public static readonly Expression<Func<T, bool>> FalseExpression = t => false;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/HashHelper.cs",
    "content": "﻿using System.Security.Cryptography;\nusing System.Text;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// HashHelper\n/// </summary>\npublic static class HashHelper\n{\n    /// <summary>\n    /// 获取哈希之后的字符串\n    /// </summary>\n    /// <param name=\"type\">哈希类型</param>\n    /// <param name=\"str\">源字符串</param>\n    /// <returns>哈希算法处理之后的字符串</returns>\n    public static string GetHashedString(HashType type, string str) => GetHashedString(type, str, Encoding.UTF8);\n\n    /// <summary>\n    /// 获取哈希之后的字符串\n    /// </summary>\n    /// <param name=\"type\">哈希类型</param>\n    /// <param name=\"str\">源字符串</param>\n    /// <param name=\"isLower\">是否是小写</param>\n    /// <returns>哈希算法处理之后的字符串</returns>\n    public static string GetHashedString(HashType type, string str, bool isLower) => GetHashedString(type, str, Encoding.UTF8, isLower);\n\n    /// <summary>\n    /// 获取哈希之后的字符串\n    /// </summary>\n    /// <param name=\"type\">哈希类型</param>\n    /// <param name=\"str\">源字符串</param>\n    /// <param name=\"key\">key</param>\n    /// <param name=\"isLower\">是否是小写</param>\n    /// <returns>哈希算法处理之后的字符串</returns>\n    public static string GetHashedString(HashType type, string str, string? key, bool isLower = false) => GetHashedString(type, str, key, Encoding.UTF8, isLower);\n\n    /// <summary>\n    /// 获取哈希之后的字符串\n    /// </summary>\n    /// <param name=\"type\">哈希类型</param>\n    /// <param name=\"str\">源字符串</param>\n    /// <param name=\"encoding\">编码类型</param>\n    /// <param name=\"isLower\">是否是小写</param>\n    /// <returns>哈希算法处理之后的字符串</returns>\n    public static string GetHashedString(HashType type, string str, Encoding encoding, bool isLower = false) => GetHashedString(type, str, null, encoding, isLower);\n\n    /// <summary>\n    /// 获取哈希之后的字符串\n    /// </summary>\n    /// <param name=\"type\">哈希类型</param>\n    /// <param name=\"str\">源字符串</param>\n    /// <param name=\"key\">key</param>\n    /// <param name=\"encoding\">编码类型</param>\n    /// <param name=\"isLower\">是否是小写</param>\n    /// <returns>哈希算法处理之后的字符串</returns>\n    public static string GetHashedString(HashType type, string str, string? key, Encoding encoding, bool isLower = false)\n    {\n        return string.IsNullOrEmpty(str) ? string.Empty : GetHashedString(type, str.GetBytes(encoding), string.IsNullOrEmpty(key) ? null : encoding.GetBytes(key!), isLower);\n    }\n\n    /// <summary>\n    /// 计算字符串Hash值\n    /// </summary>\n    /// <param name=\"type\">hash类型</param>\n    /// <param name=\"source\">source</param>\n    /// <returns>hash过的字节数组</returns>\n    public static string GetHashedString(HashType type, byte[] source) => GetHashedString(type, source, null);\n\n    /// <summary>\n    /// 计算字符串Hash值\n    /// </summary>\n    /// <param name=\"type\">hash类型</param>\n    /// <param name=\"source\">source</param>\n    /// <param name=\"isLower\">isLower</param>\n    /// <returns>hash过的字节数组</returns>\n    public static string GetHashedString(HashType type, byte[] source, bool isLower) => GetHashedString(type, source, null, isLower);\n\n    /// <summary>\n    /// 获取哈希之后的字符串\n    /// </summary>\n    /// <param name=\"type\">哈希类型</param>\n    /// <param name=\"source\">源</param>\n    /// <param name=\"key\">key</param>\n    /// <param name=\"isLower\">是否是小写</param>\n    /// <returns>哈希算法处理之后的字符串</returns>\n    public static string GetHashedString(HashType type, byte[] source, byte[]? key, bool isLower = false)\n    {\n        Guard.NotNull(source, nameof(source));\n        if (source.Length == 0)\n        {\n            return string.Empty;\n        }\n        var hashedBytes = GetHashedBytes(type, source, key);\n        return hashedBytes.ToHexString(isLower);\n    }\n\n    public static string GetHashedString(HashAlgorithmName hashAlgorithm, byte[] source, byte[]? key, bool isLower = false)\n    {\n        Guard.NotNull(source, nameof(source));\n        if (source.Length == 0) return string.Empty;\n\n#if NET9_0_OR_GREATER\n        return key is { Length: > 0 } \n            ? GetHashedString(hashAlgorithm, new ReadOnlySpan<byte>(key), new ReadOnlySpan<byte>(source), isLower) \n            : GetHashedString(hashAlgorithm, new ReadOnlySpan<byte>(source), isLower);\n#else\n        var hashedBytes = GetHashedBytes(hashAlgorithm, source, key);\n        return hashedBytes.ToHexString(isLower);\n#endif\n    }\n\n    /// <summary>\n    /// 计算字符串Hash值\n    /// </summary>\n    /// <param name=\"type\">hash类型</param>\n    /// <param name=\"str\">要hash的字符串</param>\n    /// <returns>hash过的字节数组</returns>\n    public static byte[] GetHashedBytes(HashType type, string str) => GetHashedBytes(type, str, Encoding.UTF8);\n\n    /// <summary>\n    /// 计算字符串Hash值\n    /// </summary>\n    /// <param name=\"type\">hash类型</param>\n    /// <param name=\"str\">要hash的字符串</param>\n    /// <param name=\"encoding\">编码类型</param>\n    /// <returns>hash过的字节数组</returns>\n    public static byte[] GetHashedBytes(HashType type, string str, Encoding encoding)\n    {\n        Guard.NotNull(str, nameof(str));\n        if (str == string.Empty)\n        {\n            return [];\n        }\n        var bytes = encoding.GetBytes(str);\n        return GetHashedBytes(type, bytes);\n    }\n\n    /// <summary>\n    /// 获取Hash后的字节数组\n    /// </summary>\n    /// <param name=\"type\">哈希类型</param>\n    /// <param name=\"bytes\">原字节数组</param>\n    /// <returns></returns>\n    public static byte[] GetHashedBytes(HashType type, byte[] bytes) => GetHashedBytes(type, bytes, null);\n\n    /// <summary>\n    /// 获取Hash后的字节数组\n    /// </summary>\n    /// <param name=\"type\">哈希类型</param>\n    /// <param name=\"key\">key</param>\n    /// <param name=\"bytes\">原字节数组</param>\n    /// <returns></returns>\n    public static byte[] GetHashedBytes(HashType type, byte[] bytes, byte[]? key)\n    {\n        Guard.NotNull(bytes, nameof(bytes));\n        if (bytes.Length == 0)\n        {\n            return bytes;\n        }\n\n        HashAlgorithm? algorithm = null;\n        try\n        {\n            if (key == null)\n            {\n                algorithm = type switch\n                {\n                    HashType.SHA1 => SHA1.Create(),\n                    HashType.SHA256 => SHA256.Create(),\n                    HashType.SHA384 => SHA384.Create(),\n                    HashType.SHA512 => SHA512.Create(),\n                    _ => MD5.Create()\n                };\n            }\n            else\n            {\n                algorithm = type switch\n                {\n                    HashType.SHA1 => new HMACSHA1(key),\n                    HashType.SHA256 => new HMACSHA256(key),\n                    HashType.SHA384 => new HMACSHA384(key),\n                    HashType.SHA512 => new HMACSHA512(key),\n                    _ => new HMACMD5(key)\n                };\n            }\n            return algorithm.ComputeHash(bytes);\n        }\n        finally\n        {\n            algorithm?.Dispose();\n        }\n    }\n\n    public static byte[] GetHashedBytes(HashAlgorithmName hashAlgorithm, byte[] bytes, byte[]? key)\n    {\n        Guard.NotNull(bytes, nameof(bytes));\n        if (Enum.TryParse(hashAlgorithm.Name, true, out HashType hashType))\n            return GetHashedBytes(hashType, bytes, key);\n\n        throw new ArgumentOutOfRangeException(nameof(hashAlgorithm), @$\"Unsupported hash algorithm {hashAlgorithm.Name}\");\n    }\n\n#if NET9_0_OR_GREATER\n    public static string GetHashedString(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> bytes, bool isLower = false)\n    {\n        var hashedBytes = CryptographicOperations.HashData(hashAlgorithm, bytes);\n        return hashedBytes.ToHexString(isLower);\n    }\n    \n    public static string GetHashedString(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> keys, ReadOnlySpan<byte> bytes, bool isLower = false)\n    {\n        var hashedBytes = CryptographicOperations.HmacData(hashAlgorithm, keys, bytes);\n        return hashedBytes.ToHexString(isLower);\n    }\n#endif\n}\n\n/// <summary>\n/// Hash 类型\n/// </summary>\npublic enum HashType\n{\n    /// <summary>\n    /// MD5\n    /// </summary>\n    MD5 = 0,\n\n    /// <summary>\n    /// SHA1\n    /// </summary>\n    SHA1 = 1,\n\n    /// <summary>\n    /// SHA256\n    /// </summary>\n    SHA256 = 2,\n\n    /// <summary>\n    /// SHA384\n    /// </summary>\n    SHA384 = 3,\n\n    /// <summary>\n    /// SHA512\n    /// </summary>\n    SHA512 = 4\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Hosting/AppHost.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\n\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\nusing Microsoft.Extensions.Options;\nusing System.Diagnostics;\nusing System.Runtime.ExceptionServices;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers.Hosting;\n\npublic interface IAppHost\n{\n    IConfiguration Configuration { get; }\n    IServiceProvider Services { get; }\n    Task RunAsync(CancellationToken cancellationToken = default);\n}\n\npublic sealed class AppHost : IAppHost\n{\n    private const string\n        AppHostStartingMessage = \"AppHost starting\",\n        AppHostStartedMessage = \"AppHost started. Press Ctrl+C to shut down\",\n        AppHostStoppingMessage = \"AppHost stopping\",\n        AppHostStoppedMessage = \"AppHost stopped\"\n        ;\n\n    private readonly ILogger _logger;\n    private readonly AppHostOptions _appHostOptions;\n\n    private readonly IHostedService[] _hostedServices;\n    private readonly IHostedLifecycleService[] _hostedLifecycleServices;\n\n    public AppHost(IServiceProvider services, IConfiguration configuration)\n    {\n        Services = services;\n        Configuration = configuration;\n        _logger = services.GetRequiredService<ILogger<AppHost>>();\n        _appHostOptions = services.GetRequiredService<IOptions<AppHostOptions>>().Value;\n\n        _hostedServices = services.GetServices<IHostedService>().ToArray();\n        _hostedLifecycleServices = _hostedServices.Select(x => x as IHostedLifecycleService)\n            .WhereNotNull().ToArray();\n    }\n\n    public IConfiguration Configuration { get; }\n    public ILogger Logger => _logger;\n    public IServiceProvider Services { get; }\n\n    public async Task RunAsync(CancellationToken cancellationToken = default)\n    {\n        LogAppHostMessage(AppHostStartingMessage);\n        using var hostStopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(ApplicationHelper.ExitToken, cancellationToken);\n        var waitForStopTask = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);\n        hostStopTokenSource.Token.Register(() => waitForStopTask.TrySetResult());\n        var exceptions = new List<Exception>();\n\n        var startTimeoutCts = new CancellationTokenSource(_appHostOptions.StartupTimeout);\n        var hostStartCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ApplicationHelper.ExitToken, startTimeoutCts.Token);\n        var hostStartCancellationToken = hostStartCancellationTokenSource.Token;\n        await ForeachService(_hostedLifecycleServices, hostStartCancellationToken, _appHostOptions.ServicesStartConcurrently,\n            !_appHostOptions.ServicesStartConcurrently, exceptions, async (service, cancelToken) =>\n            {\n                await service.StartingAsync(cancelToken);\n            }).ConfigureAwait(false);\n        LogAndRethrow();\n        await ForeachService(_hostedServices, hostStartCancellationToken, _appHostOptions.ServicesStartConcurrently,\n                            !_appHostOptions.ServicesStartConcurrently, exceptions, async (service, cancelToken) =>\n                            {\n                                await service.StartAsync(cancelToken);\n                            }).ConfigureAwait(false);\n        LogAndRethrow();\n        await ForeachService(_hostedLifecycleServices, hostStartCancellationToken, _appHostOptions.ServicesStartConcurrently,\n                    !_appHostOptions.ServicesStartConcurrently, exceptions, async (service, cancelToken) =>\n                    {\n                        await service.StartedAsync(cancelToken);\n                    }).ConfigureAwait(false);\n        LogAndRethrow();\n        startTimeoutCts.Dispose();\n        LogAppHostMessage(AppHostStartedMessage);\n\n        await waitForStopTask.Task.ConfigureAwait(false);\n        LogAppHostMessage(AppHostStoppingMessage);\n\n        // reverse to keep first startup last stop when not in concurrent\n        Array.Reverse(_hostedServices);\n        Array.Reverse(_hostedLifecycleServices);\n        var stopTimeoutCts = new CancellationTokenSource(_appHostOptions.ShutdownTimeout);\n        var hostStopCancellationToken = stopTimeoutCts.Token;\n        await ForeachService(_hostedLifecycleServices, hostStopCancellationToken, _appHostOptions.ServicesStopConcurrently,\n            false, exceptions, async (service, cancelToken) =>\n            {\n                await service.StoppingAsync(cancelToken);\n            }).ConfigureAwait(false);\n        await ForeachService(_hostedServices, hostStopCancellationToken, _appHostOptions.ServicesStopConcurrently,\n            false, exceptions, async (service, cancelToken) =>\n            {\n                await service.StopAsync(cancelToken);\n            }).ConfigureAwait(false);\n        await ForeachService(_hostedLifecycleServices, hostStopCancellationToken, _appHostOptions.ServicesStopConcurrently,\n            false, exceptions, async (service, cancelToken) =>\n            {\n                await service.StoppedAsync(cancelToken);\n            }).ConfigureAwait(false);\n\n        LogAppHostMessage(AppHostStoppedMessage);\n\n        // Log and abort if there are exceptions.\n        void LogAndRethrow()\n        {\n            if (exceptions is not { Count: > 0 }) return;\n\n            if (exceptions.Count == 1)\n            {\n                // Rethrow if it's a single error\n                var singleException = exceptions[0];\n                _logger.LogCritical(singleException, \"AppHost Startup exception\");\n                ExceptionDispatchInfo.Capture(singleException).Throw();\n            }\n            else\n            {\n                var ex = new AggregateException(\"One or more hosted services failed to start.\", exceptions);\n                _logger.LogCritical(ex, \"AppHost Startup exception\");\n                throw ex;\n            }\n        }\n\n        void LogAppHostMessage(string message)\n        {\n            Debug.WriteLine(message);\n            _logger.LogInformation(message);\n        }\n    }\n\n    public static AppHostBuilder CreateBuilder(AppHostBuilderSettings? settings = null)\n    {\n        return new AppHostBuilder(settings);\n    }\n\n    private static async Task ForeachService<T>(\n        T[] services,\n        CancellationToken token,\n        bool concurrent,\n        bool abortOnFirstException,\n        List<Exception> exceptions,\n        Func<T, CancellationToken, Task> operation)\n    {\n        if (services.IsNullOrEmpty()) return;\n\n        if (concurrent)\n        {\n            // The beginning synchronous portions of the implementations are run serially in registration order for\n            // performance since it is common to return Task.Completed as a noop.\n            // Any subsequent asynchronous portions are grouped together and run concurrently.\n            List<Task>? tasks = null;\n\n            foreach (var service in services)\n            {\n                Task task;\n                try\n                {\n                    task = operation(service, token);\n                }\n                catch (Exception ex)\n                {\n                    exceptions.Add(ex); // Log exception from sync method.\n                    continue;\n                }\n\n                if (task.IsCompleted)\n                {\n                    if (task.Exception is not null)\n                    {\n                        exceptions.AddRange(task.Exception.InnerExceptions); // Log exception from async method.\n                    }\n                }\n                else\n                {\n                    // The task encountered an await; add it to a list to run concurrently.\n                    tasks ??= [];\n                    tasks.Add(Task.Run(() => task, token));\n                }\n            }\n\n            if (tasks is not null)\n            {\n                var groupedTasks = Task.WhenAll(tasks);\n\n                try\n                {\n                    await groupedTasks.ConfigureAwait(false);\n                }\n                catch (Exception ex)\n                {\n                    exceptions.AddRange(groupedTasks.Exception?.InnerExceptions ?? new[] { ex }.AsEnumerable());\n                }\n            }\n        }\n        else\n        {\n            foreach (var service in services)\n            {\n                try\n                {\n                    await operation(service, token).ConfigureAwait(false);\n                }\n                catch (Exception ex)\n                {\n                    exceptions.Add(ex);\n                    if (abortOnFirstException)\n                    {\n                        return;\n                    }\n                }\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Hosting/AppHostBuilder.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\n\nnamespace WeihanLi.Common.Helpers.Hosting;\n\npublic interface IAppHostBuilder\n{\n    /// <summary>\n    /// Gets the set of key/value configuration properties.\n    /// </summary>\n    ConfigurationManager Configuration { get; }\n\n    /// <summary>\n    /// Gets a collection of logging providers for the application to compose. This is useful for adding new logging providers.\n    /// </summary>\n    ILoggingBuilder Logging { get; }\n\n    /// <summary>\n    /// Gets a collection of services for the application to compose. This is useful for adding user provided or framework provided services.\n    /// </summary>\n    IServiceCollection Services { get; }\n}\n\npublic sealed class AppHostBuilder : IAppHostBuilder\n{\n    private bool _hostBuilt;\n    private readonly ServiceCollection _serviceCollection;\n    internal AppHostBuilder(AppHostBuilderSettings? settings)\n    {\n        settings ??= new();\n        _serviceCollection = new ServiceCollection();\n        Configuration = settings.Configuration ?? new ConfigurationManager();\n\n        Logging = new LoggingBuilder(Services);\n\n        _serviceCollection.AddSingleton<IConfiguration>(Configuration);\n        _serviceCollection.AddLogging();\n    }\n\n    public ConfigurationManager Configuration { get; }\n    public ILoggingBuilder Logging { get; }\n    public IServiceCollection Services => _serviceCollection;\n\n    public AppHost Build()\n    {\n        if (_hostBuilt)\n        {\n            throw new InvalidOperationException(\"The AppHost had been created\");\n        }\n        _hostBuilt = true;\n#if NET\n        _serviceCollection.MakeReadOnly();\n#endif\n        var services = Services.BuildServiceProvider();\n        return new AppHost(services, Configuration);\n    }\n\n    private sealed class LoggingBuilder(IServiceCollection services) : ILoggingBuilder\n    {\n        public IServiceCollection Services { get; } = services;\n    }\n}\n\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Hosting/AppHostBuilderExtensions.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.DependencyInjection.Extensions;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace WeihanLi.Common.Helpers.Hosting;\n\npublic static class AppHostBuilderExtensions\n{\n    public static IAppHostBuilder AddHostedService<[DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes.PublicConstructors))] TService>(this IAppHostBuilder appHostBuilder)\n        where TService : class, IHostedService\n    {\n        Guard.NotNull(appHostBuilder);\n        appHostBuilder.Services.TryAddEnumerable(\n            ServiceDescriptor.Describe(typeof(IHostedService), typeof(TService), ServiceLifetime.Singleton)\n            );\n        return appHostBuilder;\n    }\n\n    public static IAppHostBuilder ConfigureHostOptions(this IAppHostBuilder appHostBuilder, Action<AppHostOptions> configure)\n    {\n        Guard.NotNull(appHostBuilder);\n        Guard.NotNull(configure);\n        appHostBuilder.Services.Configure(configure);\n        return appHostBuilder;\n    }\n\n    public static IAppHostBuilder ConfigureHostOptions(this IAppHostBuilder appHostBuilder, Action<AppHostOptions, IConfiguration> configure)\n    {\n        Guard.NotNull(appHostBuilder);\n        Guard.NotNull(configure);\n        appHostBuilder.Services.Configure<AppHostOptions>(options => configure(options, appHostBuilder.Configuration));\n        return appHostBuilder;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Hosting/AppHostBuilderSettings.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Configuration;\n\nnamespace WeihanLi.Common.Helpers.Hosting;\n\npublic sealed class AppHostBuilderSettings\n{\n    public ConfigurationManager? Configuration { get; set; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Hosting/AppHostOptions.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Helpers.Hosting;\n\npublic sealed class AppHostOptions\n{\n    /// <summary>\n    /// The default timeout for <see cref=\"IHostedService.StopAsync(CancellationToken)\"/>.\n    /// </summary>\n    /// <remarks>\n    /// This timeout also encompasses all host services implementing\n    /// <see cref=\"IHostedLifecycleService.StoppingAsync(CancellationToken)\"/> and\n    /// <see cref=\"IHostedLifecycleService.StoppedAsync(CancellationToken)\"/>.\n    /// </remarks>\n    public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromMinutes(2);\n\n    /// <summary>\n    /// The default timeout for <see cref=\"IAppHost.RunAsync(CancellationToken)\"/>.\n    /// </summary>\n    /// <remarks>\n    /// This timeout also encompasses all host services implementing\n    /// <see cref=\"IHostedLifecycleService.StartingAsync(CancellationToken)\"/> and\n    /// <see cref=\"IHostedLifecycleService.StartedAsync(CancellationToken)\"/>.\n    /// </remarks>\n    public TimeSpan StartupTimeout { get; set; } = TimeSpan.FromMinutes(2);\n\n    /// <summary>\n    /// Determines if the <see cref=\"IAppHost\"/> will start registered instances of <see cref=\"IHostedService\"/> concurrently or sequentially. Defaults to false.\n    /// </summary>\n    public bool ServicesStartConcurrently { get; set; }\n\n    /// <summary>\n    /// Determines if the <see cref=\"IAppHost\"/> will stop registered instances of <see cref=\"IHostedService\"/> concurrently or sequentially. Defaults to false.\n    /// </summary>\n    public bool ServicesStopConcurrently { get; set; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Hosting/BackgroundService.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Helpers.Hosting;\n\npublic abstract class BackgroundService : IHostedLifecycleService, IDisposable\n{\n    private Task? _executeTask;\n    private CancellationTokenSource? _stoppingCts;\n    public virtual Task StartAsync(CancellationToken cancellationToken)\n    {\n        // Create linked token to allow cancelling executing task from provided token\n        _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);\n\n        // Store the task we're executing\n        _executeTask = ExecuteAsync(_stoppingCts.Token);\n\n        // If the task is completed then return it, this will bubble cancellation and failure to the caller\n        if (_executeTask.IsCompleted)\n        {\n            return _executeTask;\n        }\n\n        // Otherwise it's running\n        return Task.CompletedTask;\n    }\n\n    public virtual async Task StopAsync(CancellationToken cancellationToken)\n    {\n        // Stop called without start\n        if (_executeTask == null)\n        {\n            return;\n        }\n\n        try\n        {\n            // Signal cancellation to the executing method\n#if NET\n            await _stoppingCts!.CancelAsync();\n#else\n            _stoppingCts!.Cancel();\n#endif\n        }\n        finally\n        {\n            // Wait until the task completes or the stop token triggers\n            var tcs = new TaskCompletionSource<object>();\n            using var registration = cancellationToken.Register(s => ((TaskCompletionSource<object>)s!).TrySetCanceled(), tcs);\n            // Do not await the _executeTask because cancelling it will throw an OperationCanceledException which we are explicitly ignoring\n            await Task.WhenAny(_executeTask, tcs.Task).ConfigureAwait(false);\n        }\n    }\n    public virtual Task StartingAsync(CancellationToken cancellationToken) => Task.CompletedTask;\n\n    public virtual Task StartedAsync(CancellationToken cancellationToken) => Task.CompletedTask;\n\n    public virtual Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;\n\n    public virtual Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;\n\n    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);\n\n    public virtual void Dispose()\n    {\n        _stoppingCts?.Cancel(false);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Hosting/CronBasedBackgroundService.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\nusing System.Diagnostics;\nusing System.Diagnostics.Metrics;\n\nnamespace WeihanLi.Common.Helpers.Hosting;\n\npublic abstract class CronBasedBackgroundService : BackgroundService\n{\n    protected abstract string CronExpression { get; }\n\n    protected abstract Task ExecuteTaskAsync(CancellationToken stoppingToken);\n\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        var next = CronHelper.GetNextOccurrence(CronExpression);\n        while (!stoppingToken.IsCancellationRequested && next.HasValue)\n        {\n            var now = DateTimeOffset.UtcNow;\n            if (now >= next)\n            {\n                _ = ExecuteTaskAsync(stoppingToken);\n                next = CronHelper.GetNextOccurrence(CronExpression);\n                if (!next.HasValue) break;\n            }\n            else\n            {\n                var delay = next.Value - DateTimeOffset.UtcNow;\n                await Task.Delay(delay, stoppingToken);\n            }\n        }\n    }\n}\n\npublic abstract class CronBasedBackgroundServiceWithDiagnostic : CronBasedBackgroundService\n{\n    private readonly IServiceProvider _serviceProvider;\n    private readonly Counter<int> _executeCounter;\n\n    protected CronBasedBackgroundServiceWithDiagnostic(IServiceProvider serviceProvider)\n    {\n        _serviceProvider = serviceProvider;\n        _executeCounter = DiagnosticHelper.Meter.CreateCounter<int>(\"cron-service-executed-counter\", \"count\", \"CronBasedBackgroundService execute count(status:[0: success, -1: error])\");\n        Logger = serviceProvider.GetRequiredService<ILoggerFactory>()\n            .CreateLogger(GetType());\n    }\n\n    protected ILogger Logger { get; }\n\n    protected abstract Task ExecuteTaskInternalAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken);\n\n    protected override async Task ExecuteTaskAsync(CancellationToken stoppingToken)\n    {\n        using var scope = _serviceProvider.CreateScope();\n        using var activity = DiagnosticHelper.ActivitySource.StartActivity();\n        try\n        {\n            Logger.LogInformation(\"BackgroundService execute begin\");\n            await ExecuteTaskInternalAsync(scope.ServiceProvider, stoppingToken);\n            Logger.LogInformation(\"BackgroundService execute end\");\n            if (_executeCounter.Enabled) _executeCounter.Add(1, new KeyValuePair<string, object?>(\"status\", \"0\"));\n        }\n        catch (Exception e)\n        {\n            activity?.SetStatus(ActivityStatusCode.Error, e.Message);\n            Logger.LogError(e, \"BackgroundService execute exception\");\n            if (_executeCounter.Enabled) _executeCounter.Add(1, new KeyValuePair<string, object?>(\"status\", \"-1\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Hosting/IHostedService.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Helpers.Hosting;\n\npublic interface IHostedService\n{\n    Task StartAsync(CancellationToken cancellationToken);\n    Task StopAsync(CancellationToken cancellationToken);\n}\n\npublic interface IHostedLifecycleService : IHostedService\n{\n    Task StartingAsync(CancellationToken cancellationToken);\n    Task StartedAsync(CancellationToken cancellationToken);\n    Task StoppingAsync(CancellationToken cancellationToken);\n    Task StoppedAsync(CancellationToken cancellationToken);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Hosting/TimerBaseBackgroundService.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\nusing System.Diagnostics;\nusing System.Diagnostics.Metrics;\n\n#if NET\n\nnamespace WeihanLi.Common.Helpers.Hosting;\n\npublic abstract class TimerBaseBackgroundService : BackgroundService\n{\n    protected abstract TimeSpan Period { get; }\n    protected abstract Task ExecuteTaskAsync(CancellationToken stoppingToken);\n    \n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        using var timer = new PeriodicTimer(Period);\n        while (await timer.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false))\n        {\n            await ExecuteTaskAsync(stoppingToken).ConfigureAwait(false);\n        }\n    }\n}\n\npublic abstract class TimerBaseBackgroundServiceWithDiagnostic : TimerBaseBackgroundService\n{\n    private readonly IServiceProvider _serviceProvider;\n    private readonly Counter<int> _executeCounter;\n\n    protected TimerBaseBackgroundServiceWithDiagnostic(IServiceProvider serviceProvider)\n    {\n        _serviceProvider = serviceProvider;\n        _executeCounter = DiagnosticHelper.Meter.CreateCounter<int>(\"timer-service-executed-counter\", \"count\", \"TimerBaseBackgroundService execute count(status:[0: success, -1: error])\");\n        Logger = _serviceProvider.GetRequiredService<ILoggerFactory>()\n            .CreateLogger(GetType());\n    }\n\n    protected ILogger Logger { get; }\n\n    protected abstract Task ExecuteTaskInternalAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken);\n\n    protected override async Task ExecuteTaskAsync(CancellationToken stoppingToken)\n    {\n        using var scope = _serviceProvider.CreateScope();\n        using var activity = DiagnosticHelper.ActivitySource.StartActivity();\n        try\n        {\n            Logger.LogInformation(\"BackgroundService execute begin\");\n            await ExecuteTaskInternalAsync(scope.ServiceProvider, stoppingToken);\n            Logger.LogInformation(\"BackgroundService execute end\");\n            if (_executeCounter.Enabled) _executeCounter.Add(1, new KeyValuePair<string, object?>(\"status\", \"0\"));\n        }\n        catch (Exception e)\n        {\n            activity?.SetStatus(ActivityStatusCode.Error, e.Message);\n            Logger.LogError(e, \"BackgroundService execute exception\");\n            if (_executeCounter.Enabled) _executeCounter.Add(1, new KeyValuePair<string, object?>(\"status\", \"-1\"));\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/HttpHelper.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Net;\nusing System.Text;\nusing WeihanLi.Common.Http;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// Http request helper\n/// </summary>\npublic static class HttpHelper\n{\n    #region Constants\n\n    #region UploadFileHeaderFormat\n\n    /// <summary>\n    /// Upload file File header format\n    /// 0:fileKey\n    /// 1:fileName\n    /// </summary>\n    internal const string FileHeaderFormat =\n        \"Content-Disposition: form-data; name=\\\"{0}\\\"; filename=\\\"{1}\\\"\\r\\n\" +\n        \"Content-Type: application/octet-stream\\r\\n\\r\\n\";\n\n    /// <summary>\n    /// FormDataFormat\n    /// 0:key\n    /// 1:value\n    /// 2:boundary\n    /// </summary>\n    internal const string FormDataFormat = \"\\r\\n--{2}\\r\\nContent-Disposition: form-data; name=\\\"{0}\\\";\\r\\n\\r\\n{1}\";\n\n    #endregion UploadFileHeaderFormat\n\n    #region MediaType\n\n    public const string ApplicationJsonMediaType = \"application/json\";\n    public const string ApplicationXmlMediaType = \"application/xml\";\n    public const string TextPlainMediaType = \"text/plain\";\n\n    #endregion MediaType\n\n    #region ContentType\n\n    /// <summary>\n    /// Json ContentType\n    /// </summary>\n    public const string ApplicationJsonContentType = $\"{ApplicationJsonMediaType};charset=UTF-8\";\n\n    /// <summary>\n    /// FormData ContentType\n    /// </summary>\n    public const string FormDataContentType = \"application/x-www-form-urlencoded;charset=UTF-8\";\n\n    #endregion ContentType\n\n    #endregion Constants\n\n    /// <summary>\n    /// Content Header\n    /// get latest from https://github.com/dotnet/corefx/blob/master/src/System.Net.Requests/src/System/Net/HttpWebRequest.cs#L1420\n    /// </summary>\n    public static readonly HashSet<string> WellKnownContentHeaders = new(StringComparer.OrdinalIgnoreCase)\n    {\n        HttpHeaderNames.ContentDisposition,\n        HttpHeaderNames.ContentEncoding,\n        HttpHeaderNames.ContentLanguage,\n        HttpHeaderNames.ContentLength,\n        HttpHeaderNames.ContentLocation,\n        HttpHeaderNames.ContentMD5,\n        HttpHeaderNames.ContentRange,\n        HttpHeaderNames.ContentType,\n        HttpHeaderNames.Expires,\n        HttpHeaderNames.LastModified\n    };\n\n    public static bool IsWellKnownContentHeader(string header)\n    {\n        return WellKnownContentHeaders.Contains(header);\n    }\n\n    private static readonly Lazy<HttpClient> SharedHttpClient = new(() => new(new NoProxyHttpClientHandler()\n    {\n#if NET\n        AutomaticDecompression = DecompressionMethods.All\n#else\n        AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate\n#endif\n    }));\n\n    /// <summary>\n    /// Shared HttpClient(disable cookie container, enable auto decompression)\n    /// </summary>\n    public static HttpClient HttpClient => SharedHttpClient.Value;\n\n    #region WebRequest\n\n    #region HttpGet\n\n    /// <summary>\n    /// HTTP GET请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <returns></returns>\"\n    public static string HttpGetString(string url)\n        => HttpGetString(url, null, null);\n\n    /// <summary>\n    /// HTTP GET请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"customHeaders\"></param>\n    /// <returns></returns>\"\n    public static string HttpGetString(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders)\n        => HttpGetString(url, customHeaders, null);\n\n    /// <summary>\n    /// HTTP GET请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"proxy\">proxy</param>\n    /// <param name=\"customHeaders\">customHeaders</param>\n    /// <returns></returns>\"\n    public static string HttpGetString(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders, WebProxy? proxy)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"GET\";\n\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n        return request.GetResponseString();\n    }\n\n    /// <summary>\n    /// HTTP GET请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <returns></returns>\n    public static Task<string> HttpGetStringAsync(string url)\n        => HttpGetStringAsync(url, null, null);\n\n    /// <summary>\n    /// HTTP GET请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"customHeaders\"></param>\n    /// <returns></returns>\"\n    public static Task<string> HttpGetStringAsync(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders)\n        => HttpGetStringAsync(url, customHeaders, null);\n\n    /// <summary>\n    /// HTTP GET请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"customHeaders\"></param>\n    /// <param name=\"proxy\"></param>\n    /// <returns></returns>\n    public static async Task<string> HttpGetStringAsync(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders,\n        WebProxy? proxy)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"GET\";\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n        return await request.GetResponseStringSafeAsync();\n    }\n\n    /// <summary>\n    /// HTTP GET请求，返回字节数组\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <returns></returns>\"\n    public static byte[] HttpGetForBytes(string url)\n        => HttpGetForBytes(url, null, null);\n\n    /// <summary>\n    /// HTTP GET请求，返回字节数组\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"customHeaders\">customHeaders</param>\n    /// <returns></returns>\"\n    public static byte[] HttpGetForBytes(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders)\n        => HttpGetForBytes(url, customHeaders, null);\n\n    /// <summary>\n    /// HTTP GET请求，返回字节数组\n    /// </summary>\n    /// <param name=\"url\"></param>\n    /// <param name=\"customHeaders\"></param>\n    /// <param name=\"proxy\"></param>\n    /// <returns></returns>\n    public static byte[] HttpGetForBytes(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders,\n        WebProxy? proxy)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"GET\";\n\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n\n        return request.GetResponseBytesSafe();\n    }\n\n    public static Task<byte[]> HttpGetForBytesAsync(string url) => HttpGetForBytesAsync(url, null, null);\n\n    public static Task<byte[]> HttpGetForBytesAsync(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders) => HttpGetForBytesAsync(url, customHeaders, null);\n\n    /// <summary>\n    /// HTTP GET请求，返回字节数组\n    /// </summary>\n    /// <param name=\"url\"></param>\n    /// <param name=\"customHeaders\"></param>\n    /// <param name=\"proxy\"></param>\n    /// <returns></returns>\n    public static async Task<byte[]> HttpGetForBytesAsync(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders,\n        WebProxy? proxy)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"GET\";\n\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n\n        return await request.GetResponseBytesSafeAsync();\n    }\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T HttpGetFor<T>(string url)\n        => HttpGetString(url).StringToType<T>();\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T HttpGetFor<T>(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders)\n        => HttpGetString(url, customHeaders).StringToType<T>();\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T HttpGetFor<T>(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders,\n        WebProxy? proxy)\n        => HttpGetString(url, customHeaders, proxy).StringToType<T>();\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static Task<T> HttpGetForAsync<T>(string url)\n        => HttpGetStringAsync(url).ContinueWith(result => result.Result.StringToType<T>());\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static Task<T> HttpGetForAsync<T>(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders)\n        => HttpGetStringAsync(url, customHeaders).ContinueWith(result => result.Result.StringToType<T>());\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static Task<T> HttpGetForAsync<T>(string url, IEnumerable<KeyValuePair<string, string>>? customHeaders,\n        WebProxy? proxy)\n        => HttpGetStringAsync(url, customHeaders, proxy).ContinueWith(result => result.Result.StringToType<T>());\n\n    /// <summary>\n    /// HTTP GET 请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"parameters\"> post数据字典 </param>\n    /// <returns></returns>\n    public static string HttpGetString(string url, IDictionary<string, string>? parameters)\n    {\n        if (parameters is { Count: > 0 })\n        {\n            url = url + (url.IndexOf('?') < 0 ? \"?\" : \"&\") + string.Join(\"&\", parameters.Select(p => $\"{WebUtility.UrlEncode(p.Key)}={WebUtility.UrlEncode(p.Value)}\"));\n        }\n        return HttpGetString(url);\n    }\n\n    /// <summary>\n    /// HTTP GET 请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"parameters\"> post数据字典 </param>\n    /// <returns></returns>\n    public static async Task<string> HttpGetStringAsync(string url, IDictionary<string, string>? parameters)\n    {\n        if (parameters is { Count: > 0 })\n        {\n            url = url + (url.IndexOf('?') < 0 ? \"?\" : \"&\") + string.Join(\"&\", parameters.Select(p => $\"{WebUtility.UrlEncode(p.Key)}={WebUtility.UrlEncode(p.Value)}\"));\n        }\n        return await HttpGetStringAsync(url);\n    }\n\n    public static byte[] HttpGetForBytes(string url, IDictionary<string, string>? parameters)\n    {\n        if (parameters is { Count: > 0 })\n        {\n            url = url + (url.IndexOf('?') < 0 ? \"?\" : \"&\") + string.Join(\"&\", parameters.Select(p => $\"{p.Key}={p.Value}\"));\n        }\n        return HttpGetForBytes(url);\n    }\n\n    public static async Task<byte[]> HttpGetForBytesAsync(string url, IDictionary<string, string>? parameters)\n    {\n        if (parameters is { Count: > 0 })\n        {\n            url = url + (url.IndexOf('?') < 0 ? \"?\" : \"&\") + string.Join(\"&\", parameters.Select(p => $\"{p.Key}={p.Value}\"));\n        }\n        return await HttpGetForBytesAsync(url);\n    }\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T HttpGetFor<T>(string url, IDictionary<string, string>? parameters)\n    {\n        if (parameters.HasValue())\n        {\n            url = url + (url.IndexOf('?') < 0 ? \"?\" : \"&\") + string.Join(\"&\", parameters.Select(p => $\"{p.Key}={p.Value}\"));\n        }\n        return HttpGetFor<T>(url);\n    }\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static async Task<T> HttpGetForAsync<T>(string url, IDictionary<string, string>? parameters)\n    {\n        if (parameters.HasValue())\n        {\n            url = url + (url.IndexOf('?') < 0 ? \"?\" : \"&\") + string.Join(\"&\", parameters.Select(p => $\"{p.Key}={p.Value}\"));\n        }\n        return await HttpGetForAsync<T>(url);\n    }\n\n    #endregion HttpGet\n\n    #region HttpPost\n\n    /// <summary>\n    /// 获取 post 请求的 ContentType\n    /// </summary>\n    /// <param name=\"isJsonFormat\">请求参数是否是Json格式</param>\n    /// <returns></returns>\n    private static string GetContentType(bool isJsonFormat) => isJsonFormat ? ApplicationJsonContentType : FormDataContentType;\n\n    /// <summary>\n    /// HTTP POST 请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"parameters\"> post数据字典 </param>\n    /// <returns></returns>\n    public static string HttpPost(string url, IDictionary<string, string>? parameters)\n        => HttpPost(url,\n            Encoding.UTF8.GetBytes(string.Join(\"&\",\n                parameters?.Select(p => $\"{WebUtility.UrlEncode(p.Key)}={WebUtility.UrlEncode(p.Value)}\") ?? Array.Empty<string>())), false);\n\n    /// <summary>\n    /// HTTP POST 请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"parameters\"> post数据字典 </param>\n    /// <returns></returns>\n    public static Task<string> HttpPostAsync(string url, IDictionary<string, string>? parameters)\n        => HttpPostAsync(url,\n        Encoding.UTF8.GetBytes(string.Join(\"&\",\n    parameters?.Select(p => $\"{WebUtility.UrlEncode(p.Key)}={WebUtility.UrlEncode(p.Value)}\") ?? Array.Empty<string>())), false);\n\n    /// <summary>\n    /// Http\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"url\"></param>\n    /// <param name=\"data\"></param>\n    /// <returns></returns>\n    public static string HttpPostJson<T>(string url, T data)\n    => HttpPost(url, Encoding.UTF8.GetBytes(data.ToJson()));\n\n    public static string HttpPostJson<T>(string url, T data, Encoding encoding)\n    => HttpPost(url, encoding.GetBytes(data.ToJson()));\n\n    /// <summary>\n    /// HttpPostJsonAsync\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"url\"></param>\n    /// <param name=\"data\"></param>\n    /// <returns></returns>\n    public static Task<string> HttpPostJsonAsync<T>(string url, T data)\n        => HttpPostAsync(url, Encoding.UTF8.GetBytes(data.ToJson()));\n\n    public static Task<string> HttpPostJsonAsync<T>(string url, T data, Encoding encoding)\n        => HttpPostAsync(url, encoding.GetBytes(data.ToJson()));\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static TResponse HttpPostJsonFor<TRequest, TResponse>(string url, TRequest data)\n    => HttpPostFor<TResponse>(url, Encoding.UTF8.GetBytes(data.ToJson()), true);\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static TResponse HttpPostJsonFor<TRequest, TResponse>(string url, TRequest data, Encoding encoding)\n    => HttpPostFor<TResponse>(url, encoding.GetBytes(data.ToJson()), true);\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static Task<TResponse> HttpPostJsonForAsync<TRequest, TResponse>(string url, TRequest data)\n        => HttpPostForAsync<TResponse>(url, Encoding.UTF8.GetBytes(data.ToJson()), true);\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static Task<TResponse> HttpPostJsonForAsync<TRequest, TResponse>(string url, TRequest data, Encoding encoding)\n        => HttpPostForAsync<TResponse>(url, encoding.GetBytes(data.ToJson()), true);\n\n    /// <summary>\n    /// HTTP POST 请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"postData\"> post数据 </param>\n    /// <param name=\"isJsonFormat\"> 是否是json格式数据 </param>\n    /// <param name=\"customHeaders\"></param>\n    /// <param name=\"proxy\"></param>\n    /// <returns></returns>\n    public static string HttpPost(string url, byte[] postData, bool isJsonFormat = true, IEnumerable<KeyValuePair<string, string>>? customHeaders = null, WebProxy? proxy = null)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"POST\";\n\n        request.ContentType = GetContentType(isJsonFormat);\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(\"REFERER\"))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(\"User-Agent\"))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n        var postStream = request.GetRequestStream();\n        postStream.Write(postData);\n        return request.GetResponseStringSafe();\n    }\n\n    /// <summary>\n    /// HTTP POST 请求，返回字符串\n    /// </summary>\n    /// <param name=\"url\"> url </param>\n    /// <param name=\"postData\"> post数据 </param>\n    /// <param name=\"isJsonFormat\"> 是否是json格式数据 </param>\n    /// <param name=\"customHeaders\"></param>\n    /// <param name=\"proxy\"></param>\n    /// <returns></returns>\n    public static async Task<string> HttpPostAsync(string url, byte[] postData, bool isJsonFormat = true, IEnumerable<KeyValuePair<string, string>>? customHeaders = null, WebProxy? proxy = null)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"POST\";\n\n        request.ContentType = GetContentType(isJsonFormat);\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n        var postStream = await request.GetRequestStreamAsync();\n        await postStream.WriteAsync(postData);\n        return await request.GetResponseStringSafeAsync();\n    }\n\n    public static async Task<string> HttpPostAsync(string url, byte[] postData, string contentType, IEnumerable<KeyValuePair<string, string>>? customHeaders = null, WebProxy? proxy = null)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"POST\";\n\n        request.ContentType = contentType;\n\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n\n        var postStream = await request.GetRequestStreamAsync();\n        await postStream.WriteAsync(postData);\n        return await request.GetResponseStringSafeAsync();\n    }\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T HttpPostFor<T>(string url, byte[] postData, bool isJsonFormat)\n        => HttpPost(url, postData, isJsonFormat).StringToType<T>();\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static async Task<T> HttpPostForAsync<T>(string url, byte[] postData, bool isJsonFormat)\n        => (await HttpPostAsync(url, postData, isJsonFormat)).StringToType<T>();\n\n    public static byte[] HttpPostForBytes(string url, byte[] postData, bool isJsonFormat, IEnumerable<KeyValuePair<string, string>>? customHeaders = null, WebProxy? proxy = null)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"POST\";\n\n        request.ContentType = GetContentType(isJsonFormat);\n\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n\n        var postStream = request.GetRequestStream();\n        postStream.Write(postData);\n\n        return request.GetResponseBytesSafe();\n    }\n\n    public static byte[] HttpPostForBytes(string url, byte[] postData, string contentType, IEnumerable<KeyValuePair<string, string>>? customHeaders = null, WebProxy? proxy = null)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"POST\";\n\n        request.ContentType = contentType;\n\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n        var postStream = request.GetRequestStream();\n        postStream.Write(postData);\n        return request.GetResponseBytesSafe();\n    }\n\n    public static async Task<byte[]> HttpPostForBytesAsync(string url, byte[] postData, bool isJsonFormat, IEnumerable<KeyValuePair<string, string>>? customHeaders = null, WebProxy? proxy = null)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"POST\";\n\n        request.ContentType = GetContentType(isJsonFormat);\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n        var postStream = await request.GetRequestStreamAsync();\n        await postStream.WriteAsync(postData);\n        return await request.GetResponseBytesSafeAsync();\n    }\n\n    public static async Task<byte[]> HttpPostForBytesAsync(string url, byte[] postData, string contentType, IEnumerable<KeyValuePair<string, string>>? customHeaders = null, WebProxy? proxy = null)\n    {\n        var request = WebRequest.CreateHttp(url);\n        request.UserAgent = GetUserAgent();\n        request.Method = \"POST\";\n\n        request.ContentType = contentType;\n        if (null != customHeaders)\n        {\n            foreach (var header in customHeaders)\n            {\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.Referer))\n                {\n                    request.Referer = header.Value;\n                    continue;\n                }\n                if (header.Key.EqualsIgnoreCase(HttpHeaderNames.UserAgent))\n                {\n                    request.UserAgent = header.Value;\n                    continue;\n                }\n                request.Headers.Add(header.Key, header.Value);\n            }\n        }\n\n        if (null != proxy)\n        {\n            request.Proxy = proxy;\n        }\n        var postStream = await request.GetRequestStreamAsync();\n        await postStream.WriteAsync(postData);\n        return await request.GetResponseBytesSafeAsync();\n    }\n\n    /// <summary>\n    /// PostFile\n    /// <see href=\"https://stackoverflow.com/questions/566462/upload-files-with-httpwebrequest-multipart-form-data\"></see>\n    /// <see href=\"http://www.cnblogs.com/greenerycn/archive/2010/05/15/csharp_http_post.html\"></see>\n    /// </summary>\n    /// <param name=\"url\">post url</param>\n    /// <param name=\"filePath\">filePath</param>\n    /// <param name=\"fileKey\">fileKey in form,default is \"file\"</param>\n    /// <param name=\"formFields\">other form fields</param>\n    /// <param name=\"headers\">headers</param>\n    /// <returns></returns>\n    public static string HttpPostFile(string url, string filePath, string fileKey = \"file\",\n        IEnumerable<KeyValuePair<string, string>>? formFields = null, IEnumerable<KeyValuePair<string, string>>? headers = null)\n        => HttpPostFile(url, Path.GetFileName(filePath), File.ReadAllBytes(filePath), fileKey, formFields, headers);\n\n    /// <summary>\n    /// PostFile\n    /// </summary>\n    /// <param name=\"url\">post url</param>\n    /// <param name=\"fileName\">fileName</param>\n    /// <param name=\"fileBytes\">fileBytes</param>\n    /// <param name=\"fileKey\">fileKey in form,default is \"file\"</param>\n    /// <param name=\"formFields\">other form fields</param>\n    /// <param name=\"headers\">request headers</param>\n    /// <returns></returns>\n    public static string HttpPostFile(string url, string fileName, byte[] fileBytes, string fileKey = \"file\", IEnumerable<KeyValuePair<string, string>>? formFields = null, IEnumerable<KeyValuePair<string, string>>? headers = null)\n    {\n        var request = WebRequest.CreateHttp(url);\n        var boundary = $\"----------------------------{DateTime.UtcNow.Ticks:X}\";\n\n        request.ContentType = $\"multipart/form-data; boundary={boundary}\";\n        request.Method = \"POST\";\n        request.KeepAlive = true;\n\n        if (headers != null)\n        {\n            foreach (var header in headers)\n            {\n                request.Headers[header.Key] = header.Value;\n            }\n        }\n\n        var boundarybytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}\\r\\n\");\n        var endBoundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}--\");\n\n        using var memStream = new MemoryStream();\n        if (formFields != null)\n        {\n            foreach (var pair in formFields)\n            {\n                memStream.Write(Encoding.UTF8.GetBytes(string.Format(FormDataFormat, pair.Key, pair.Value, boundary)));\n            }\n        }\n\n        memStream.Write(boundarybytes);\n\n        memStream.Write(Encoding.UTF8.GetBytes(string.Format(FileHeaderFormat, fileKey, fileName)));\n\n        memStream.Write(fileBytes);\n\n        memStream.Write(endBoundaryBytes);\n\n        request.ContentLength = memStream.Length;\n\n        using (var requestStream = request.GetRequestStream())\n        {\n            memStream.Seek(0, SeekOrigin.Begin);\n            requestStream.Write(memStream.ToArray());\n        }\n\n        return request.GetResponseStringSafe();\n    }\n\n    /// <summary>\n    /// PostMultiFile\n    /// </summary>\n    /// <param name=\"url\">post url</param>\n    /// <param name=\"filePaths\">files</param>\n    /// <param name=\"formFields\">other form fields</param>\n    /// <param name=\"headers\">request headers</param>\n    /// <returns></returns>\n    public static string HttpPostFile(string url, IEnumerable<string> filePaths,\n        IEnumerable<KeyValuePair<string, string>>? formFields = null, IEnumerable<KeyValuePair<string, string>>? headers = null)\n        => HttpPostFile(url,\n            filePaths.Select(_ => new KeyValuePair<string, byte[]>(Path.GetFileName(_), File.ReadAllBytes(_))),\n            formFields, headers);\n\n    /// <summary>\n    /// PostMultiFile\n    /// </summary>\n    /// <param name=\"url\">post url</param>\n    /// <param name=\"files\">files</param>\n    /// <param name=\"formFields\">other form fields</param>\n    /// <param name=\"headers\">request headers</param>\n    /// <returns></returns>\n    public static string HttpPostFile(string url, IEnumerable<KeyValuePair<string, byte[]>> files, IEnumerable<KeyValuePair<string, string>>? formFields = null, IEnumerable<KeyValuePair<string, string>>? headers = null)\n    {\n        var boundary = $\"----------------------------{DateTime.UtcNow.Ticks:X}\";\n\n        var request = WebRequest.CreateHttp(url);\n        request.ContentType = $\"multipart/form-data; boundary={boundary}\";\n        request.Method = \"POST\";\n        request.KeepAlive = true;\n\n        if (headers != null)\n        {\n            foreach (var header in headers)\n            {\n                request.Headers[header.Key] = header.Value;\n            }\n        }\n\n        var boundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}\\r\\n\");\n        var endBoundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}--\");\n\n        using var memStream = new MemoryStream();\n        if (formFields != null)\n        {\n            foreach (var pair in formFields)\n            {\n                memStream.Write(Encoding.UTF8.GetBytes(string.Format(FormDataFormat, pair.Key, pair.Value, boundary)));\n            }\n        }\n\n        foreach (var file in files)\n        {\n            memStream.Write(boundaryBytes);\n\n            memStream.Write(Encoding.UTF8.GetBytes(string.Format(FileHeaderFormat, Path.GetFileNameWithoutExtension(file.Key), file.Key)));\n            memStream.Write(file.Value);\n        }\n\n        memStream.Write(endBoundaryBytes);\n        request.ContentLength = memStream.Length;\n\n        using (var requestStream = request.GetRequestStream())\n        {\n            memStream.Seek(0, SeekOrigin.Begin);\n            requestStream.Write(memStream.ToArray());\n        }\n\n        return request.GetResponseStringSafe();\n    }\n\n    /// <summary>\n    /// PostFileAsync\n    /// https://stackoverflow.com/questions/566462/upload-files-with-httpwebrequest-multipart-form-data\n    /// http://www.cnblogs.com/greenerycn/archive/2010/05/15/csharp_http_post.html\n    /// </summary>\n    /// <param name=\"url\">post url</param>\n    /// <param name=\"filePath\">filePath</param>\n    /// <param name=\"fileKey\">fileKey in form,default is \"file\"</param>\n    /// <param name=\"formFields\">other form fields</param>\n    /// <param name=\"headers\">request headers</param>\n    /// <returns>response text</returns>\n    public static Task<string> HttpPostFileAsync(string url, string filePath, string fileKey = \"file\",\n        IEnumerable<KeyValuePair<string, string>>? formFields = null, IEnumerable<KeyValuePair<string, string>>? headers = null)\n        => HttpPostFileAsync(url, Path.GetFileName(filePath), File.ReadAllBytes(filePath), fileKey, formFields, headers);\n\n    /// <summary>\n    /// PostFileAsync\n    /// </summary>\n    /// <param name=\"url\">post url</param>\n    /// <param name=\"fileName\">fileName</param>\n    /// <param name=\"fileBytes\">fileBytes</param>\n    /// <param name=\"fileKey\">fileKey in form,default is \"file\"</param>\n    /// <param name=\"formFields\">other form fields</param>\n    /// <param name=\"headers\">request headers</param>\n    /// <returns></returns>\n    public static async Task<string> HttpPostFileAsync(string url, string fileName, byte[] fileBytes, string fileKey = \"file\", IEnumerable<KeyValuePair<string, string>>? formFields = null, IEnumerable<KeyValuePair<string, string>>? headers = null)\n    {\n        var boundary = $\"----------------------------{DateTime.UtcNow.Ticks:X}\";\n\n        var request = WebRequest.CreateHttp(url);\n        request.ContentType = $\"multipart/form-data; boundary={boundary}\";\n        request.Method = \"POST\";\n        request.KeepAlive = true;\n        if (headers != null)\n        {\n            foreach (var header in headers)\n            {\n                request.Headers[header.Key] = header.Value;\n            }\n        }\n        var boundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}\\r\\n\");\n        var endBoundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}--\");\n\n        using var memStream = new MemoryStream();\n        if (formFields != null)\n        {\n            foreach (var pair in formFields)\n            {\n                memStream.Write(Encoding.UTF8.GetBytes(string.Format(FormDataFormat, pair.Key, pair.Value, boundary)));\n            }\n        }\n\n        await memStream.WriteAsync(boundaryBytes);\n\n        await memStream.WriteAsync(Encoding.UTF8.GetBytes(string.Format(FileHeaderFormat, fileKey, fileName)));\n\n        await memStream.WriteAsync(fileBytes);\n\n        await memStream.WriteAsync(endBoundaryBytes);\n\n        request.ContentLength = memStream.Length;\n\n        using (var requestStream = await request.GetRequestStreamAsync())\n        {\n            memStream.Seek(0, SeekOrigin.Begin);\n            await requestStream.WriteAsync(memStream.ToArray());\n        }\n\n        return await request.GetResponseStringSafeAsync();\n    }\n\n    /// <summary>\n    /// Post Multi File Async\n    /// </summary>\n    /// <param name=\"url\">post url</param>\n    /// <param name=\"filePaths\">files</param>\n    /// <param name=\"formFields\">other form fields</param>\n    /// <param name=\"headers\">request headers</param>\n    /// <returns></returns>\n    public static Task<string> HttpPostFileAsync(string url, IEnumerable<string> filePaths,\n        IEnumerable<KeyValuePair<string, string>>? formFields = null, IEnumerable<KeyValuePair<string, string>>? headers = null)\n        => HttpPostFileAsync(url,\n            filePaths.Select(_ => new KeyValuePair<string, byte[]>(Path.GetFileName(_), File.ReadAllBytes(_))),\n            formFields, headers);\n\n    /// <summary>\n    /// Post Multi File Async\n    /// </summary>\n    /// <param name=\"url\">post url</param>\n    /// <param name=\"files\">files</param>\n    /// <param name=\"formFields\">other form fields</param>\n    /// <param name=\"headers\">request headers</param>\n    /// <returns></returns>\n    public static async Task<string> HttpPostFileAsync(string url, IEnumerable<KeyValuePair<string, byte[]>> files, IEnumerable<KeyValuePair<string, string>>? formFields = null, IEnumerable<KeyValuePair<string, string>>? headers = null)\n    {\n        var boundary = $\"----------------------------{DateTime.UtcNow.Ticks:X}\";\n\n        var request = WebRequest.CreateHttp(url);\n        request.ContentType = $\"multipart/form-data; boundary={boundary}\";\n        request.Method = \"POST\";\n        request.KeepAlive = true;\n        if (headers != null)\n        {\n            foreach (var header in headers)\n            {\n                request.Headers[header.Key] = header.Value;\n            }\n        }\n        var boundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}\\r\\n\");\n        var endBoundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}--\");\n\n        using var memStream = new MemoryStream();\n        if (formFields != null)\n        {\n            foreach (var pair in formFields)\n            {\n                memStream.Write(\n                    Encoding.UTF8.GetBytes(string.Format(FormDataFormat, pair.Key, pair.Value, boundary)));\n            }\n        }\n\n        foreach (var file in files)\n        {\n            await memStream.WriteAsync(boundaryBytes);\n\n            await memStream.WriteAsync(Encoding.UTF8.GetBytes(\n                string.Format(FileHeaderFormat, Path.GetFileNameWithoutExtension(file.Key), file.Key)));\n            await memStream.WriteAsync(file.Value);\n        }\n\n        await memStream.WriteAsync(endBoundaryBytes);\n\n        request.ContentLength = memStream.Length;\n\n        using var requestStream = await request.GetRequestStreamAsync();\n        memStream.Seek(0, SeekOrigin.Begin);\n        await requestStream.WriteAsync(memStream.ToArray());\n\n        return await request.GetResponseStringSafeAsync();\n    }\n\n    #endregion HttpPost\n\n    #endregion WebRequest\n\n    #region UserAgents\n\n    private static readonly string[] MobileUserAgents =\n    [\n        \"Mozilla/5.0 (iPhone 84; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.0 MQQBrowser/7.8.0 Mobile/14G60 Safari/8536.25 MttCustomUA/2 QBWebViewType/1 WKType/1\",\n        \"Mozilla/5.0 (Linux; Android 7.0; STF-AL10 Build/HUAWEISTF-AL10; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043508 Safari/537.36 V1_AND_SQ_7.2.0_730_YYB_D QQ/7.2.0.3270 NetType/4G WebP/0.3.0 Pixel/1080\",\n        \"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Mobile/14G60 MicroMessenger/6.5.18 NetType/WIFI Language/en\",\n        \"Mozilla/5.0 (Linux; Android 5.1.1; vivo Xplay5A Build/LMY47V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/48.0.2564.116 Mobile Safari/537.36 T7/9.3 baiduboxapp/9.3.0.10 (Baidu; P1 5.1.1)\",\n        \"Mozilla/5.0 (Linux; U; Android 7.0; zh-cn; STF-AL00 Build/HUAWEISTF-AL00) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/7.9 Mobile Safari/537.36\",\n        \"Mozilla/5.0 (iPhone 92; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 MQQBrowser/7.7.2 Mobile/14F89 Safari/8536.25 MttCustomUA/2 QBWebViewType/1 WKType/1\",\n        \"Mozilla/5.0 (Linux; U; Android 6.0.1; zh-CN; SM-C7000 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/40.0.2214.89 UCBrowser/11.6.2.948 Mobile Safari/537.36\",\n        \"Mozilla/5.0 (Linux; U; Android 5.1.1; zh-cn; MI 4S Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/9.1.3\",\n        \"Mozilla/5.0 (Linux; Android 7.0; MIX Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043508 Safari/537.36 V1_AND_SQ_7.2.0_730_YYB_D QQ/7.2.0.3270 NetType/WIFI WebP/0.3.0 Pixel/1080\",\n        \"Mozilla/5.0 (Linux; Android 7.1.1; MI 6 Build/NMF26X; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043508 Safari/537.36 MicroMessenger/6.5.13.1100 NetType/WIFI Language/zh_CN\",\n        \"Mozilla/5.0 (Linux; U; Android 7.0; zh-cn; MIX Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/9.2.2\",\n        \"Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; MIX Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/7.8 Mobile Safari/537.36\"\n    ];\n\n    private static readonly string[] DesktopUserAgents =\n    [\n        \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.103 Safari/537.36\",\n        \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.38 Safari/537.36\",\n        \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36\",\n        \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\",\n        \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36\",\n        \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36\",\n        \"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0\",\n        \"Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12.388 Version/12.17\",\n        \"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)\",\n        \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\",\n        \"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0\",\n        \"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36\",\n        \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36\",\n        \"Mozilla/5.0 (Windows NT 5.2) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30\",\n        \"Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0\",\n        \"Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.9.168 Version/11.50\",\n        \"Mozilla/5.0 (Windows; U; Windows NT 5.1; ) AppleWebKit/534.12 (KHTML, like Gecko) Maxthon/3.0 Safari/534.12\"\n    ];\n\n    private static readonly string[] WeChatUserAgents =\n    [\n        \"Mozilla/5.0 (Linux; Android 6.0; 1503-M02 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile MQQBrowser/6.2 TBS/036558 Safari/537.36 MicroMessenger/6.3.25.861 NetType/WIFI Language/zh_CN\",\n        \"Mozilla/5.0 (Linux; Android 5.1; OPPO R9tm Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043220 Safari/537.36 MicroMessenger/6.5.7.1041 NetType/4G Language/zh_CN\",\n        \"Mozilla/5.0 (iPhone; CPU iPhone OS 9_3 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13E233 MicroMessenger/6.3.15 NetType/WIFI Language/zh_CN\",\n        \"Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Mobile/14D27 MicroMessenger/6.5.6 NetType/4G Language/zh_CN\"\n    ];\n\n    /// <summary>\n    /// GetUserAgent\n    /// </summary>\n    /// <param name=\"isMobileUserAgent\">isMobileUserAgent</param>\n    /// <returns>UserAgent</returns>\n    public static string GetUserAgent(bool isMobileUserAgent = false)\n    {\n        return isMobileUserAgent ? MobileUserAgents[SecurityHelper.Random.Next(MobileUserAgents.Length)] : DesktopUserAgents[SecurityHelper.Random.Next(DesktopUserAgents.Length)];\n    }\n\n    /// <summary>\n    /// GetWeChatUserAgent\n    /// </summary>\n    /// <returns></returns>\n    public static string GetWeChatUserAgent() => WeChatUserAgents[SecurityHelper.Random.Next(WeChatUserAgents.Length)];\n\n    #endregion UserAgents\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/InMemoryStream.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Collections.Concurrent;\nusing System.Runtime.CompilerServices;\nusing WeihanLi.Common.Models;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic class StreamMessage<T>\n{\n    public required T Id { get; init; }\n    public DateTimeOffset Timestamp { get; set; }\n    public Dictionary<string, string> Fields { get => field; set => field = Guard.NotNull(value); } = [];\n    public Dictionary<string, object> Properties { get => field; set => field = Guard.NotNull(value); } = [];\n}\n\npublic class StreamInfo<T>\n{\n    public required T MinId { get; set; }\n    public DateTimeOffset MinTimestamp { get; set; }\n    public required T MaxId { get; set; }\n    public DateTimeOffset MaxTimestamp { get; set; }\n    public int Count { get; set; }\n}\n\npublic class StreamGroupInfo<T>\n{\n    public required string GroupName { get; set; }\n    public required T Offset { get; set; }\n}\n\npublic interface IStream<T>\n{\n    string StreamName { get; }\n\n    Task AddAsync(T id, Dictionary<string, string> fields, DateTimeOffset? timestamp = null, Dictionary<string, object>? properties = null, CancellationToken cancellationToken = default);\n    IAsyncEnumerable<StreamMessage<T>> FetchAsync(T lastId, int count, Ordering order = default, CancellationToken cancellationToken = default);\n\n    Task<int> CountAsync(T? min = default, T? max = default, RangeInclusion inclusion = default, CancellationToken cancellationToken = default);\n\n    Task<StreamInfo<T>> InfoAsync(CancellationToken cancellationToken = default);\n    Task<IReadOnlyCollection<StreamGroupInfo<T>>> GroupsAsync(CancellationToken cancellationToken = default);\n    Task<StreamGroupInfo<T>?> GroupInfoAsync(string groupName, CancellationToken cancellationToken = default);\n    Task AddGroupAsync(string groupName, T offset, CancellationToken cancellationToken = default);\n    Task<bool> RemoveGroupAsync(string groupName, CancellationToken cancellationToken = default);\n    Task AckAsync(string groupName, T id, CancellationToken cancellationToken = default);\n}\n\npublic sealed class InMemoryStream<T>(string name, IComparer<T>? comparer = null) : IStream<T>\n{\n    private readonly List<StreamMessage<T>> _messages = new();\n    private readonly ConcurrentDictionary<string, StreamGroupInfo<T>> _groups = new();\n    private readonly IComparer<T> _comparer = comparer ?? Comparer<T>.Default;\n\n    public string StreamName => name;\n\n    public Task AckAsync(string groupName, T id, CancellationToken cancellationToken = default)\n    {\n        if (!_groups.TryGetValue(groupName, out var groupInfo))\n        {\n            throw new InvalidOperationException($\"Group [{groupName}] not exists\");\n        }\n\n        groupInfo.Offset = id;\n        return Task.CompletedTask;\n    }\n\n    public Task AddAsync(T id, Dictionary<string, string> fields, DateTimeOffset? timestamp = null, Dictionary<string, object>? properties = null, CancellationToken cancellationToken = default)\n    {\n        var message = new StreamMessage<T>\n        {\n            Id = id,\n            Fields = fields,\n            Timestamp = timestamp ?? DateTimeOffset.Now\n        };\n\n        if (properties is { Count: > 0 })\n        {\n            foreach (var item in properties)\n            {\n                message.Properties[item.Key] = item.Value;\n            }\n        }\n\n        _messages.Add(message);\n\n        return Task.CompletedTask;\n    }\n\n    public Task AddGroupAsync(string groupName, T offset, CancellationToken cancellationToken = default)\n    {\n        if (_groups.ContainsKey(groupName))\n        {\n            throw new InvalidOperationException($\"Group [{groupName}] already exists\");\n        }\n\n        _groups[groupName] = new StreamGroupInfo<T>()\n        {\n            GroupName = groupName,\n            Offset = offset\n        };\n        return Task.CompletedTask;\n    }\n\n    public Task<int> CountAsync(T? min = default, T? max = default, RangeInclusion inclusion = default, CancellationToken cancellationToken = default)\n    {\n        var count = _messages.Count;\n        if (min != null || max != null)\n        {\n            count = _messages.Count(item =>\n            {\n                var id = item.Id;\n                var isInRange = true;\n                if (min != null)\n                {\n                    isInRange = inclusion.HasFlag(RangeInclusion.IncludeLowerBound) ? _comparer.Compare(id, min) >= 0 : _comparer.Compare(id, min) > 0;\n                }\n                if (max != null)\n                {\n                    isInRange = inclusion.HasFlag(RangeInclusion.IncludeUpperBound) ? _comparer.Compare(id, max) <= 0 : _comparer.Compare(id, max) < 0;\n                }\n                return isInRange;\n            });\n        }\n        return Task.FromResult(count);\n    }\n\n    public async IAsyncEnumerable<StreamMessage<T>> FetchAsync(T lastId, int count, Ordering order = Ordering.Ascending, [EnumeratorCancellation] CancellationToken cancellationToken = default)\n    {\n        var messages = order == Ordering.Ascending ? _messages.OrderBy(item => item.Id) : _messages.OrderByDescending(item => item.Id);\n        var fetchedCount = 0;\n        foreach (var message in messages)\n        {\n            if (fetchedCount >= count)\n            {\n                yield break;\n            }\n            await Task.CompletedTask;\n            if (_comparer.Compare(message.Id, lastId) > 0)\n            {\n                yield return message;\n                fetchedCount++;\n            }\n        }\n    }\n\n    public Task<StreamGroupInfo<T>?> GroupInfoAsync(string groupName, CancellationToken cancellationToken = default)\n    {\n        if (_groups.TryGetValue(groupName, out var groupInfo))\n        {\n            return Task.FromResult<StreamGroupInfo<T>?>(groupInfo);\n        }\n\n        return Task.FromResult<StreamGroupInfo<T>?>(null);\n    }\n\n    public Task<IReadOnlyCollection<StreamGroupInfo<T>>> GroupsAsync(CancellationToken cancellationToken = default)\n    {\n        return _groups.Values.AsReadOnly().WrapTask();\n    }\n\n    public Task<StreamInfo<T>> InfoAsync(CancellationToken cancellationToken = default)\n    {\n        var streamInfo = new StreamInfo<T>\n        {\n            MinId = default!,\n            MinTimestamp = default,\n            MaxId = default!,\n            MaxTimestamp = default,\n            Count = _messages.Count\n        };\n\n        if (_messages.Count > 0)\n        {\n            var minMessage = _messages.MinBy(item => item.Id, _comparer);\n            var maxMessage = _messages.MaxBy(item => item.Id, _comparer);\n            ArgumentNullException.ThrowIfNull(minMessage);\n            ArgumentNullException.ThrowIfNull(maxMessage);\n\n            streamInfo.MinId = minMessage.Id;\n            streamInfo.MinTimestamp = minMessage.Timestamp;\n            streamInfo.MaxId = maxMessage.Id;\n            streamInfo.MaxTimestamp = maxMessage.Timestamp;\n        }\n\n        return streamInfo.WrapTask();\n    }\n\n    public Task<bool> RemoveGroupAsync(string groupName, CancellationToken cancellationToken = default)\n    {\n        return _groups.TryRemove(groupName, out _).WrapTask();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/InterlockedHelper.cs",
    "content": "﻿namespace WeihanLi.Common.Helpers;\n\npublic static class InterlockedHelper\n{\n    public static int Read(ref int value)\n    {\n        return Interlocked.CompareExchange(ref value, 0, 0);\n    }\n\n    public static T? Read<T>(ref T? value) where T : class\n    {\n        return Interlocked.CompareExchange(ref value, null, null);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/InteropHelper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Runtime.InteropServices;\n\nnamespace WeihanLi.Common.Helpers;\n\n// https://github.dev/dotnet/sdk/blob/5c99629b15ef721440e61007b88088d0cc1d3c49/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs#L182\ninternal static class InteropHelper\n{\n    public static readonly bool RunningOnWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);\n\n    public static class Unix\n    {\n        // Ansi marshaling on Unix is actually UTF8\n        // ReSharper disable InconsistentNaming\n        private const CharSet UTF8 = CharSet.Ansi;\n        private static string? PtrToStringUTF8(IntPtr ptr) => Marshal.PtrToStringAnsi(ptr);\n\n        [DllImport(\"libc\", CharSet = UTF8, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]\n        private static extern IntPtr realpath(string path, IntPtr buffer);\n\n        [DllImport(\"libc\", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]\n        private static extern void free(IntPtr ptr);\n\n        public static string? RealPath(string path)\n        {\n            var ptr = realpath(path, IntPtr.Zero);\n            var result = PtrToStringUTF8(ptr);\n            free(ptr);\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/InvokeHelper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class InvokeHelper\n{\n    static InvokeHelper()\n    {\n        OnInvokeException = ex => ConsoleHelper.WriteLineWithColor(ex.ToString(), ConsoleColor.DarkRed);\n\n        #region Exit event register\n\n        // https://newlifex.com/blood/elegant_exit\n        // https://github.com/NewLifeX/X/blob/e65dfa0998ec393804f3f793f333c237110d890e/NewLife.Core/Model/Host.cs#L61\n        // https://github.com/dotnet/runtime/blob/940b332ad04e58862febe019788a5b21e266ea10/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.notnetcoreapp.cs\n        AppDomain.CurrentDomain.ProcessExit += InvokeExitHandler;\n        Console.CancelKeyPress += InvokeExitHandler;\n#if NETCOREAPP\n        System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx => InvokeExitHandler(ctx, null);\n#endif\n#if NET\n        // https://github.com/dotnet/runtime/blob/940b332ad04e58862febe019788a5b21e266ea10/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.netcoreapp.cs\n        PosixSignalRegistration.Create(PosixSignal.SIGINT, ctx => InvokeExitHandler(ctx, null));\n        PosixSignalRegistration.Create(PosixSignal.SIGQUIT, ctx => InvokeExitHandler(ctx, null));\n        PosixSignalRegistration.Create(PosixSignal.SIGTERM, ctx => InvokeExitHandler(ctx, null));\n#endif\n\n        #endregion ExitHandler\n    }\n\n    #region Profile\n\n    public static double Profile(Action action)\n    {\n        Guard.NotNull(action, nameof(action));\n        var stopwatch = ValueStopwatch.StartNew();\n        action();\n        return stopwatch.Elapsed.TotalMilliseconds;\n    }\n\n    public static double Profile<T>(Action<T> action, T t)\n    {\n        Guard.NotNull(action, nameof(action));\n        var stopwatch = ValueStopwatch.StartNew();\n        action(t);\n        return stopwatch.Elapsed.TotalMilliseconds;\n    }\n\n    public static double Profile<T1, T2>(Action<T1, T2> action, T1 t1, T2 t2)\n    {\n        Guard.NotNull(action, nameof(action));\n        var stopwatch = ValueStopwatch.StartNew();\n        action(t1, t2);\n        return stopwatch.Elapsed.TotalMilliseconds;\n    }\n\n    public static double Profile<T1, T2, T3>(Action<T1, T2, T3> action, T1 t1, T2 t2, T3 t3)\n    {\n        var stopwatch = ValueStopwatch.StartNew();\n        action(t1, t2, t3);\n        return stopwatch.Elapsed.TotalMilliseconds;\n    }\n\n    public static async Task<double> ProfileAsync(Func<Task> action)\n    {\n        var stopwatch = ValueStopwatch.StartNew();\n        await action();\n        return stopwatch.Elapsed.TotalMilliseconds;\n    }\n\n    public static async Task<double> ProfileAsync<T>(Func<T, Task> func, T t)\n    {\n        var stopwatch = ValueStopwatch.StartNew();\n        await func(t);\n        return stopwatch.Elapsed.TotalMilliseconds;\n    }\n\n    public static async Task<double> ProfileAsync<T1, T2>(Func<T1, T2, Task> func, T1 t1, T2 t2)\n    {\n        var stopwatch = ValueStopwatch.StartNew();\n        await func(t1, t2);\n        return stopwatch.Elapsed.TotalMilliseconds;\n    }\n\n    public static async Task<double> ProfileAsync<T1, T2, T3>(Func<T1, T2, T3, Task> func, T1 t1, T2 t2, T3 t3)\n    {\n        var stopwatch = ValueStopwatch.StartNew();\n        await func(t1, t2, t3);\n        return stopwatch.Elapsed.TotalMilliseconds;\n    }\n\n    #endregion Profile\n\n    #region TryInvoke\n\n    public static Action<Exception>? OnInvokeException { get; set; }\n\n    private static readonly Lock ExitLock = new();\n    private static volatile bool _exited;\n    private static readonly Lazy<CancellationTokenSource> LazyCancellationTokenSource = new();\n    private static void InvokeExitHandler(object? sender, EventArgs? args)\n    {\n        if (_exited) return;\n        // no need to configure since we're going to exit\n        //         if (args is ConsoleCancelEventArgs consoleCancelEventArgs)\n        //         {\n        //             consoleCancelEventArgs.Cancel = true;\n        //         }\n        // #if NET\n        //         if (sender is PosixSignalContext posixSignalContext)\n        //         {\n        //             posixSignalContext.Cancel = true;\n        //         }\n        // #endif\n        lock (ExitLock)\n        {\n            if (_exited) return;\n            Debug.WriteLine(\"exiting...\");\n            if (LazyCancellationTokenSource.IsValueCreated)\n            {\n                LazyCancellationTokenSource.Value.Cancel(false);\n                LazyCancellationTokenSource.Value.Dispose();\n            }\n            Debug.WriteLine(\"exited\");\n            _exited = true;\n        }\n    }\n\n    internal static CancellationToken GetExitTokenInternal() => LazyCancellationTokenSource.Value.Token;\n\n    public static void TryInvoke(Action action, int? maxRetryCount = null)\n    {\n        Guard.NotNull(action);\n\n        var maxRetry = maxRetryCount.GetValueOrDefault();\n    invoke:\n\n        try\n        {\n            action();\n        }\n        catch (Exception ex)\n        {\n            OnInvokeException?.Invoke(ex);\n            if (maxRetry-- > 0)\n                goto invoke;\n        }\n    }\n\n    public static void TryInvoke<T>(Action<T> action, T t, int? maxRetryCount = null)\n    {\n        Guard.NotNull(action);\n\n        var maxRetry = maxRetryCount.GetValueOrDefault();\n    invoke:\n\n        try\n        {\n            action(t);\n        }\n        catch (Exception ex)\n        {\n            OnInvokeException?.Invoke(ex);\n            if (maxRetry-- > 0)\n                goto invoke;\n        }\n    }\n\n    public static void TryInvoke<T1, T2>(Action<T1, T2> action, T1 t1, T2 t2, int? maxRetryCount = null)\n    {\n        Guard.NotNull(action);\n\n        var maxRetry = maxRetryCount.GetValueOrDefault();\n    invoke:\n\n        try\n        {\n            action(t1, t2);\n        }\n        catch (Exception ex)\n        {\n            OnInvokeException?.Invoke(ex);\n            if (maxRetry-- > 0)\n                goto invoke;\n        }\n    }\n\n    public static async Task TryInvokeAsync<T1, T2>(Func<T1, T2, Task> func, T1 t1, T2 t2, int? maxRetryCount = null)\n    {\n        Guard.NotNull(func);\n\n        var maxRetry = maxRetryCount.GetValueOrDefault();\n    invoke:\n\n        try\n        {\n            await func(t1, t2);\n        }\n        catch (Exception ex)\n        {\n            OnInvokeException?.Invoke(ex);\n            if (maxRetry-- > 0)\n                goto invoke;\n        }\n    }\n\n    public static void TryInvoke<T1, T2, T3>(Action<T1, T2, T3> action, T1 t1, T2 t2, T3 t3, int? maxRetryCount = null)\n    {\n        Guard.NotNull(action);\n\n        var maxRetry = maxRetryCount.GetValueOrDefault();\n    invoke:\n\n        try\n        {\n            action(t1, t2, t3);\n        }\n        catch (Exception ex)\n        {\n            OnInvokeException?.Invoke(ex);\n            if (maxRetry-- > 0)\n                goto invoke;\n        }\n    }\n\n    public static async Task TryInvokeAsync(Func<Task> func, int? maxRetryCount = null)\n    {\n        Guard.NotNull(func);\n\n        var maxRetry = maxRetryCount.GetValueOrDefault();\n    invoke:\n\n\n        try\n        {\n            await func();\n        }\n        catch (Exception ex)\n        {\n            OnInvokeException?.Invoke(ex);\n            if (maxRetry-- > 0)\n                goto invoke;\n        }\n    }\n\n    public static async Task TryInvokeAsync<T>(Func<T, Task> func, T t, int? maxRetryCount = null)\n    {\n        Guard.NotNull(func);\n\n        var maxRetry = maxRetryCount.GetValueOrDefault();\n    invoke:\n\n        try\n        {\n            await func(t);\n        }\n        catch (Exception ex)\n        {\n            OnInvokeException?.Invoke(ex);\n            if (maxRetry-- > 0)\n                goto invoke;\n        }\n    }\n\n    public static async Task TryInvokeAsync<T1, T2, T3>(Func<T1, T2, T3, Task> func, T1 t1, T2 t2, T3 t3, int? maxRetryCount = null)\n    {\n        Guard.NotNull(func);\n\n        var maxRetry = maxRetryCount.GetValueOrDefault();\n    invoke:\n\n        try\n        {\n            await func(t1, t2, t3);\n        }\n        catch (Exception ex)\n        {\n            OnInvokeException?.Invoke(ex);\n\n            if (maxRetry-- > 0)\n                goto invoke;\n        }\n    }\n\n    #endregion TryInvoke\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/JsonHelper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n#if NET\n\nusing System.Text.Encodings.Web;\nusing System.Text.Json;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class JsonHelper\n{\n    public static JsonSerializerOptions WebOptions => new(JsonSerializerDefaults.Web);\n    public static JsonSerializerOptions UnsafeEncoderOptions => \n        new JsonSerializerOptions(JsonSerializerDefaults.General).WithUnsafeEncoder();\n    public static JsonSerializerOptions WriteIntendedUnsafeEncoderOptions => \n        new JsonSerializerOptions(JsonSerializerDefaults.General).WithWriteIntended().WithUnsafeEncoder();\n\n    public static JsonSerializerOptions WithWriteIntended(this JsonSerializerOptions jsonSerializerOptions)\n    {\n        Guard.NotNull(jsonSerializerOptions);\n        jsonSerializerOptions.WriteIndented = true;\n        return jsonSerializerOptions;\n    }\n    \n    public static JsonSerializerOptions WithUnsafeEncoder(this JsonSerializerOptions jsonSerializerOptions)\n    {\n        Guard.NotNull(jsonSerializerOptions);\n        jsonSerializerOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;\n        return jsonSerializerOptions;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/LogHelper.cs",
    "content": "﻿using WeihanLi.Common.Logging;\n\nnamespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// LogHelper\n/// Logging\n/// </summary>\npublic static class LogHelper\n{\n    private static ILogHelperFactory LogFactory { get; set; } = NullLogHelperFactory.Instance;\n\n    public static void ConfigureLogging(Action<ILogHelperLoggingBuilder> configureAction)\n    {\n        var loggingBuilder = new LogHelperLoggingBuilder();\n        Guard.NotNull(configureAction, nameof(configureAction)).Invoke(loggingBuilder);\n        LogFactory = loggingBuilder.Build();\n    }\n\n    public static ILogHelperLogger GetLogger<T>() => LogFactory.GetLogger(typeof(T));\n\n    public static ILogHelperLogger GetLogger(Type type) => LogFactory.GetLogger(type);\n\n    public static ILogHelperLogger GetLogger(string categoryName)\n    {\n        Guard.NotNullOrEmpty(categoryName, nameof(categoryName));\n        return LogFactory.CreateLogger(categoryName);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/MapHelper.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class MapHelper\n{\n    public static TTarget Map<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TSource, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TTarget>(TSource source) where TTarget : new()\n    {\n        Guard.NotNull(source);\n        var sourceType = typeof(TSource);\n        var destinationType = typeof(TTarget);\n\n        var properties = CacheUtil.GetTypeProperties(destinationType);\n        var sourceProps = CacheUtil.GetTypeProperties(sourceType)\n            .Where(x => properties.Any(_ => _.Name.EqualsIgnoreCase(x.Name)))\n            .ToArray();\n\n        var result = new TTarget();\n\n        if (properties.Length > 0)\n        {\n            foreach (var property in properties)\n            {\n                var sourceProperty = sourceProps.FirstOrDefault(p => p.Name.EqualsIgnoreCase(property.Name));\n                if (sourceProperty == null)\n                {\n                    continue;\n                }\n\n                var propGetter = sourceProperty.GetValueGetter();\n                if (propGetter != null)\n                {\n                    property.GetValueSetter()?.Invoke(result, propGetter.Invoke(source));\n                }\n            }\n        }\n        return result;\n    }\n\n    public static TTarget MapWith<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TSource, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TTarget>(TSource source, params string[] propertiesToMap) where TTarget : new()\n    {\n        Guard.NotNull(source);\n        var sourceType = typeof(TSource);\n        var destinationType = typeof(TTarget);\n        var result = new TTarget();\n        var properties = CacheUtil.GetTypeProperties(destinationType)\n            .Where(p => propertiesToMap.Any(_ => string.Equals(_, p.Name, StringComparison.OrdinalIgnoreCase)))\n            .ToArray();\n        var sourceProps = CacheUtil.GetTypeProperties(sourceType)\n            .Where(x => propertiesToMap.Any(_ => _.EqualsIgnoreCase(x.Name)))\n            .ToArray();\n\n        if (properties.Length > 0)\n        {\n            foreach (var property in properties)\n            {\n                var sourceProperty = sourceProps.FirstOrDefault(p => p.Name.EqualsIgnoreCase(property.Name));\n                if (sourceProperty == null || !sourceProperty.CanRead || !property.CanWrite)\n                {\n                    continue;\n                }\n\n                var propGetter = sourceProperty.GetValueGetter();\n                if (propGetter != null)\n                {\n                    property.GetValueSetter()?.Invoke(result, propGetter.Invoke(source));\n                }\n            }\n        }\n\n        return result;\n    }\n\n    public static TTarget MapWithout<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TSource, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TTarget>(TSource source, params string[] propertiesNoMap) where TTarget : new()\n    {\n        Guard.NotNull(source);\n        var sourceType = typeof(TSource);\n        var destinationType = typeof(TTarget);\n\n        var properties = CacheUtil.GetTypeProperties(destinationType)\n            .Where(p => !propertiesNoMap.Any(_ => string.Equals(_, p.Name, StringComparison.Ordinal)))\n            .ToArray();\n        var sourceProps = CacheUtil.GetTypeProperties(sourceType)\n            .Where(x => !properties.Any(_ => _.Name.EqualsIgnoreCase(x.Name)))\n            .ToArray();\n\n        var result = new TTarget();\n\n        if (properties.Length > 0)\n        {\n            foreach (var property in properties)\n            {\n                var sourceProperty = sourceProps.FirstOrDefault(p => p.Name.EqualsIgnoreCase(property.Name));\n                if (sourceProperty == null || !sourceProperty.CanRead || !property.CanWrite)\n                {\n                    continue;\n                }\n                var propGetter = sourceProperty.GetValueGetter();\n                if (propGetter != null)\n                {\n                    property.GetValueSetter()?.Invoke(result, propGetter.Invoke(source));\n                }\n            }\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/NetHelper.cs",
    "content": "﻿using System.Net;\nusing System.Net.NetworkInformation;\n\nnamespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// IPNetwork\n/// https://source.dot.net/#Microsoft.AspNetCore.HttpOverrides/IPNetwork.cs,ab4d458482303384\n/// </summary>\ninternal sealed class IPNetwork\n{\n    public IPNetwork(string cidr)\n    {\n        Guard.NotNullOrWhiteSpace(cidr);\n        var arr = cidr.Split(Separator, StringSplitOptions.RemoveEmptyEntries);\n        if (arr.Length == 0 || arr.Length > 2)\n        {\n            throw new ArgumentException(@\"Invalid cidr format\", nameof(cidr));\n        }\n        if (!IPAddress.TryParse(arr[0], out var ipAddress))\n        {\n            throw new ArgumentException(@\"Invalid ip format\", nameof(cidr));\n        }\n\n        PrefixLength = 0;\n        if (arr.Length == 2)\n        {\n            if (!int.TryParse(arr[1], out var prefixLength))\n            {\n                throw new ArgumentException(@\"Invalid cidr format\", nameof(cidr));\n            }\n            PrefixLength = prefixLength;\n        }\n\n        Prefix = ipAddress;\n        PrefixBytes = Prefix.GetAddressBytes();\n        Mask = CreateMask();\n    }\n\n    public IPNetwork(string ipPrefix, int prefixLength) : this(IPAddress.Parse(ipPrefix), prefixLength)\n    {\n    }\n\n    public IPNetwork(IPAddress prefix, int prefixLength)\n    {\n        Prefix = prefix;\n        PrefixLength = prefixLength;\n        PrefixBytes = Prefix.GetAddressBytes();\n        Mask = CreateMask();\n    }\n\n    public IPAddress Prefix { get; }\n\n    private byte[] PrefixBytes { get; }\n\n    /// <summary>\n    /// The CIDR notation of the subnet mask\n    /// </summary>\n    public int PrefixLength { get; }\n\n    private byte[] Mask { get; }\n\n    internal static readonly char[] Separator = ['/'];\n\n    public bool Contains(IPAddress address)\n    {\n        if (Prefix.AddressFamily != address.AddressFamily)\n        {\n            return false;\n        }\n\n        var addressBytes = address.GetAddressBytes();\n        for (var i = 0; i < PrefixBytes.Length && Mask[i] != 0; i++)\n        {\n            if (PrefixBytes[i] != (addressBytes[i] & Mask[i]))\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    private byte[] CreateMask()\n    {\n        var mask = new byte[PrefixBytes.Length];\n        var remainingBits = PrefixLength;\n        var i = 0;\n        while (remainingBits >= 8)\n        {\n            mask[i] = 0xFF;\n            i++;\n            remainingBits -= 8;\n        }\n        if (remainingBits > 0)\n        {\n            mask[i] = (byte)(0xFF << (8 - remainingBits));\n        }\n\n        return mask;\n    }\n}\n\npublic static class NetHelper\n{\n    /// <summary>\n    /// get a random port not used\n    /// </summary>\n    /// <param name=\"min\">min port, 10000 by default</param>\n    /// <param name=\"max\">max, 65535</param>\n    /// <returns></returns>\n    public static int GetRandomPort(int min = 10240, int max = 65535)\n    {\n        if (min < 1024 || min >= 65535)\n        {\n            min = 10240;\n        }\n        if (max < min || max > 65535)\n        {\n            max = 65535;\n        }\n\n        int randomPort;\n        do\n        {\n            randomPort = SecurityHelper.Random.Next(min, max);\n        } while (IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners().Any(p => p.Port == randomPort));\n        return randomPort;\n    }\n\n    private static readonly Lazy<IPNetwork> PrivateAddressBlockANetwork = new(() => new IPNetwork(\"10.0.0.0/8\"));\n    private static readonly Lazy<IPNetwork> PrivateAddressBlockBNetwork = new(() => new IPNetwork(\"172.16.0.0/12\"));\n    private static readonly Lazy<IPNetwork> PrivateAddressBlockCNetwork = new(() => new IPNetwork(\"192.168.0.0/16\"));\n\n    /// <summary>\n    /// whether the ip is a private ip\n    /// </summary>\n    /// <param name=\"ip\">ipAddress</param>\n    /// <returns></returns>\n    public static bool IsPrivateIP(string ip) => IsPrivateIP(IPAddress.Parse(ip));\n\n    /// <summary>\n    /// whether the ip is a private ip\n    /// </summary>\n    /// <param name=\"ipAddress\">ipAddress</param>\n    /// <returns></returns>\n    public static bool IsPrivateIP(IPAddress ipAddress)\n    {\n        return PrivateAddressBlockANetwork.Value.Contains(ipAddress)\n               || PrivateAddressBlockBNetwork.Value.Contains(ipAddress)\n               || PrivateAddressBlockCNetwork.Value.Contains(ipAddress)\n            ;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/NewFuncHelper.cs",
    "content": "﻿using System.Linq.Expressions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class NewFuncHelper<T>\n{\n    /// <summary>\n    /// CreateNewInstance func\n    /// T need to have a parameter less constructor\n    /// </summary>\n    public static readonly Func<T> Instance = Expression.Lambda<Func<T>>\n    (\n        Expression.New(typeof(T))\n    ).Compile();\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/PeriodBatching/BatchedConnectionStatus.cs",
    "content": "﻿// Copyright 2013-2016 Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// https://github.com/serilog/serilog-sinks-periodicbatching\nnamespace WeihanLi.Common.Helpers.PeriodBatching;\n\n/// <summary>\n/// Manages reconnection period and transient fault response for PeriodicBatching.\n/// During normal operation an object of this type will simply echo the configured batch transmission\n/// period. When availability fluctuates, the class tracks the number of failed attempts, each time\n/// increasing the interval before reconnection is attempted (up to a set maximum) and at predefined\n/// points indicating that either the current batch, or entire waiting queue, should be dropped. This\n/// Serves two purposes - first, a loaded receiver may need a temporary reduction in traffic while coming\n/// back online. Second, the sender needs to account for both bad batches (the first fault response) and\n/// also overproduction (the second, queue-dropping response). In combination these should provide a\n/// reasonable delivery effort but ultimately protect the sender from memory exhaustion.\n/// </summary>\n/// <remarks>\n/// Currently used only by PeriodicBatching, but may\n/// provide the basis for a \"smart\" exponential backoff timer. There are other factors to consider\n/// including the desire to send batches \"when full\" rather than continuing to buffer, and so-on.\n/// </remarks>\ninternal sealed class BatchedConnectionStatus\n{\n    private static readonly TimeSpan MinimumBackoffPeriod = TimeSpan.FromSeconds(5);\n    private static readonly TimeSpan MaximumBackoffInterval = TimeSpan.FromMinutes(10);\n    private const int FailuresBeforeDroppingBatch = 8;\n    private const int FailuresBeforeDroppingQueue = 10;\n    private readonly TimeSpan _period;\n    private int _failuresSinceSuccessfulBatch;\n\n    public BatchedConnectionStatus(TimeSpan period)\n    {\n        if (period < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(period), \"The batching period must be a positive timespan\");\n\n        _period = period;\n    }\n\n    public void MarkSuccess()\n    {\n        _failuresSinceSuccessfulBatch = 0;\n    }\n\n    public void MarkFailure()\n    {\n        ++_failuresSinceSuccessfulBatch;\n    }\n\n    public TimeSpan NextInterval\n    {\n        get\n        {\n            // Available, and first failure, just try the batch interval\n            if (_failuresSinceSuccessfulBatch <= 1) return _period;\n\n            // Second failure, start ramping up the interval - first 2x, then 4x, ...\n            var backoffFactor = Math.Pow(2, (_failuresSinceSuccessfulBatch - 1));\n\n            // If the period is ridiculously short, give it a boost so we get some\n            // visible backoff.\n            var backoffPeriod = Math.Max(_period.Ticks, MinimumBackoffPeriod.Ticks);\n\n            // The \"ideal\" interval\n            var backedOff = (long)(backoffPeriod * backoffFactor);\n\n            // Capped to the maximum interval\n            var cappedBackoff = Math.Min(MaximumBackoffInterval.Ticks, backedOff);\n\n            // Unless that's shorter than the period, in which case we'll just apply the period\n            var actual = Math.Max(_period.Ticks, cappedBackoff);\n\n            return TimeSpan.FromTicks(actual);\n        }\n    }\n\n    public bool ShouldDropBatch => _failuresSinceSuccessfulBatch >= FailuresBeforeDroppingBatch;\n\n    public bool ShouldDropQueue => _failuresSinceSuccessfulBatch >= FailuresBeforeDroppingQueue;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/PeriodBatching/PeriodicBatching.cs",
    "content": "﻿// Copyright 2013-2016 Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics;\n\nnamespace WeihanLi.Common.Helpers.PeriodBatching;\n\n/// <summary>\n/// Base class for sinks that log events in batches. Batching is\n/// triggered asynchronously on a timer.\n/// </summary>\n/// <remarks>\n/// To avoid unbounded memory growth, events are discarded after attempting\n/// to send a batch, regardless of whether the batch succeeded or not. Implementers\n/// that want to change this behavior need to either implement from scratch, or\n/// embed retry logic in the batch emitting functions.\n/// </remarks>\npublic abstract class PeriodicBatching<TEvent> : IDisposable where TEvent : class\n{\n    private readonly int _batchSizeLimit;\n    private readonly BoundedConcurrentQueue<TEvent> _queue;\n    private readonly BatchedConnectionStatus _status;\n    private readonly Queue<TEvent> _waitingBatch = new();\n    private readonly Lock _stateLock = new();\n    private readonly PortableTimer _timer;\n    private bool _unloading;\n    private bool _started;\n\n    /// <summary>\n    /// Construct a sink posting to the specified database.\n    /// </summary>\n    /// <param name=\"batchSizeLimit\">The maximum number of events to include in a single batch.</param>\n    /// <param name=\"period\">The time to wait between checking for event batches.</param>\n    protected PeriodicBatching(int batchSizeLimit, TimeSpan period)\n    {\n        _batchSizeLimit = batchSizeLimit;\n        _queue = new BoundedConcurrentQueue<TEvent>();\n        _status = new BatchedConnectionStatus(period);\n\n        _timer = new PortableTimer(_ => OnTick());\n    }\n\n    /// <summary>\n    /// Construct a sink posting to the specified database.\n    /// </summary>\n    /// <param name=\"batchSizeLimit\">The maximum number of events to include in a single batch.</param>\n    /// <param name=\"period\">The time to wait between checking for event batches.</param>\n    /// <param name=\"queueLimit\">Maximum number of events in the queue.</param>\n    protected PeriodicBatching(int batchSizeLimit, TimeSpan period, int queueLimit)\n        : this(batchSizeLimit, period)\n    {\n        _queue = new BoundedConcurrentQueue<TEvent>(queueLimit);\n    }\n\n    private void CloseAndFlush()\n    {\n        lock (_stateLock)\n        {\n            if (!_started || _unloading)\n                return;\n\n            _unloading = true;\n        }\n\n        _timer.Dispose();\n\n        // This is the place where SynchronizationContext.Current is unknown and can be != null\n        // so we prevent possible deadlocks here for sync-over-async downstream implementations\n        PeriodicBatching<TEvent>.ResetSyncContextAndWait(OnTick);\n    }\n\n    private static void ResetSyncContextAndWait(Func<Task> taskFactory)\n    {\n        var prevContext = SynchronizationContext.Current;\n        SynchronizationContext.SetSynchronizationContext(null);\n        try\n        {\n            taskFactory().Wait();\n        }\n        finally\n        {\n            SynchronizationContext.SetSynchronizationContext(prevContext);\n        }\n    }\n\n    /// <summary>\n    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.\n    /// </summary>\n    /// <filterpriority>2</filterpriority>\n    public void Dispose()\n    {\n        Dispose(true);\n    }\n\n    /// <summary>\n    /// Free resources held by the sink.\n    /// </summary>\n    /// <param name=\"disposing\">If true, called because the object is being disposed; if false,\n    /// the object is being disposed from the finalizer.</param>\n    protected virtual void Dispose(bool disposing)\n    {\n        if (!disposing)\n            return;\n\n        CloseAndFlush();\n    }\n\n    /// <summary>\n    /// Emit a batch of log events, running to completion synchronously.\n    /// </summary>\n    /// <param name=\"events\">The events to emit.</param>\n    /// <remarks>Override either <see cref=\"EmitBatch\"/> or <see cref=\"EmitBatchAsync\"/>,\n    /// not both.</remarks>\n    protected virtual void EmitBatch(IEnumerable<TEvent> events)\n    {\n    }\n\n    /// <summary>\n    /// Emit a batch of log events, running asynchronously.\n    /// </summary>\n    /// <param name=\"events\">The events to emit.</param>\n    /// <remarks>Override either <see cref=\"EmitBatchAsync\"/> or <see cref=\"EmitBatch\"/>,\n    /// not both. </remarks>\n#pragma warning disable 1998\n\n    protected virtual async Task EmitBatchAsync(IEnumerable<TEvent> events)\n#pragma warning restore 1998\n    {\n        EmitBatch(events);\n    }\n\n    private async Task OnTick()\n    {\n        try\n        {\n            bool batchWasFull;\n            do\n            {\n                while (_waitingBatch.Count < _batchSizeLimit &&\n                    _queue.TryDequeue(out var next))\n                {\n                    if (CanInclude(next))\n                        _waitingBatch.Enqueue(next);\n                }\n\n                if (_waitingBatch.Count == 0)\n                {\n                    await OnEmptyBatchAsync();\n                    return;\n                }\n\n                await EmitBatchAsync(_waitingBatch);\n\n                batchWasFull = _waitingBatch.Count >= _batchSizeLimit;\n                _waitingBatch.Clear();\n                _status.MarkSuccess();\n            }\n            while (batchWasFull); // Otherwise, allow the period to elapse\n        }\n        catch (Exception ex)\n        {\n            Debug.WriteLine(\"Exception while emitting periodic batch from {0}: {1}\", this, ex);\n            _status.MarkFailure();\n        }\n        finally\n        {\n            if (_status.ShouldDropBatch)\n                _waitingBatch.Clear();\n\n            if (_status.ShouldDropQueue)\n            {\n                while (_queue.TryDequeue(out _)) { }\n            }\n\n            lock (_stateLock)\n            {\n                if (!_unloading)\n                    SetTimer(_status.NextInterval);\n            }\n        }\n    }\n\n    private void SetTimer(TimeSpan interval)\n    {\n        _timer.Start(interval);\n    }\n\n    /// <summary>\n    /// Emit the provided log event to the sink. If the sink is being disposed or\n    /// the app domain unloaded, then the event is ignored.\n    /// </summary>\n    /// <param name=\"event\">Log event to emit.</param>\n    /// <exception cref=\"ArgumentNullException\">The event is null.</exception>\n    /// <remarks>\n    /// The sink implements the contract that any events whose Emit() method has\n    /// completed at the time of sink disposal will be flushed (or attempted to,\n    /// depending on app domain state).\n    /// </remarks>\n    public void Emit(TEvent @event)\n    {\n        Guard.NotNull(@event);\n\n        if (_unloading)\n            return;\n\n        if (!_started)\n        {\n            lock (_stateLock)\n            {\n                if (_unloading) return;\n                if (!_started)\n                {\n                    // Special handling to try to get the first event across as quickly\n                    // as possible to show we're alive!\n                    _queue.TryEnqueue(@event);\n                    _started = true;\n                    SetTimer(TimeSpan.Zero);\n                    return;\n                }\n            }\n        }\n\n        _queue.TryEnqueue(@event);\n    }\n\n    /// <summary>\n    /// Determine whether a queued log event should be included in the batch. If\n    /// an override returns false, the event will be dropped.\n    /// </summary>\n    /// <param name=\"evt\"></param>\n    /// <returns></returns>\n    protected virtual bool CanInclude(TEvent? evt)\n    {\n        return true;\n    }\n\n    /// <summary>\n    /// Allows derived sinks to perform periodic work without requiring additional threads\n    /// or timers (thus avoiding additional flush/shut-down complexity).\n    /// </summary>\n    /// <remarks>Override either <see cref=\"OnEmptyBatch\"/> or <see cref=\"OnEmptyBatchAsync\"/>,\n    /// not both. </remarks>\n    protected virtual void OnEmptyBatch()\n    {\n    }\n\n    /// <summary>\n    /// Allows derived sinks to perform periodic work without requiring additional threads\n    /// or timers (thus avoiding additional flush/shut-down complexity).\n    /// </summary>\n    /// <remarks>Override either <see cref=\"OnEmptyBatchAsync\"/> or <see cref=\"OnEmptyBatch\"/>,\n    /// not both. </remarks>\n#pragma warning disable 1998\n\n    protected virtual async Task OnEmptyBatchAsync()\n#pragma warning restore 1998\n    {\n        OnEmptyBatch();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/PeriodBatching/PortableTimer.cs",
    "content": "﻿// Copyright 2013-2016 Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics;\n\nnamespace WeihanLi.Common.Helpers.PeriodBatching;\n\ninternal sealed class PortableTimer : IDisposable\n{\n    private readonly Lock _stateLock = new();\n    private readonly Func<CancellationToken, Task> _onTick;\n    private readonly CancellationTokenSource _cancel = new();\n    private readonly Timer _timer;\n    private bool _running;\n    private bool _disposed;\n\n    public PortableTimer(Func<CancellationToken, Task> onTick)\n    {\n        _onTick = Guard.NotNull(onTick);\n\n        _timer = new Timer(_ => OnTick(), null, Timeout.Infinite, Timeout.Infinite);\n    }\n\n    public void Start(TimeSpan interval)\n    {\n        if (interval < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(interval));\n\n        lock (_stateLock)\n        {\n            if (_disposed)\n                throw new ObjectDisposedException(nameof(PortableTimer));\n\n            _timer.Change(interval, Timeout.InfiniteTimeSpan);\n        }\n    }\n\n    private async void OnTick()\n    {\n        try\n        {\n            lock (_stateLock)\n            {\n                if (_disposed)\n                {\n                    return;\n                }\n\n                // There's a little bit of raciness here, but it's needed to support the\n                // current API, which allows the tick handler to reenter and set the next interval.\n\n                if (_running)\n                {\n                    Monitor.Wait(_stateLock);\n\n                    if (_disposed)\n                    {\n                        return;\n                    }\n                }\n\n                _running = true;\n            }\n\n            if (!_cancel.Token.IsCancellationRequested)\n            {\n                await _onTick(_cancel.Token);\n            }\n        }\n        catch (OperationCanceledException tcx)\n        {\n            Debug.WriteLine(\"The timer was canceled during invocation: {0}\", tcx);\n        }\n        finally\n        {\n            lock (_stateLock)\n            {\n                _running = false;\n                Monitor.PulseAll(_stateLock);\n            }\n        }\n    }\n\n    public void Dispose()\n    {\n        _cancel.Cancel();\n\n        lock (_stateLock)\n        {\n            if (_disposed)\n            {\n                return;\n            }\n\n            while (_running)\n            {\n                Monitor.Wait(_stateLock);\n            }\n\n            _timer.Dispose();\n            _disposed = true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/PipelineBuilder.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class PipelineBuilder\n{\n    public static IPipelineBuilder<TContext> Create<TContext>()\n    {\n        return new PipelineBuilder<TContext>(c => { });\n    }\n\n    public static IPipelineBuilder<TContext> Create<TContext>(Action<TContext> completeAction)\n    {\n        return new PipelineBuilder<TContext>(completeAction);\n    }\n\n    public static IAsyncPipelineBuilder<TContext> CreateAsync<TContext>()\n    {\n        return new AsyncPipelineBuilder<TContext>(c => Task.CompletedTask);\n    }\n\n    public static IAsyncPipelineBuilder<TContext> CreateAsync<TContext>(Func<TContext, Task> completeFunc)\n    {\n        return new AsyncPipelineBuilder<TContext>(completeFunc);\n    }\n\n    public static IValueAsyncPipelineBuilder<TContext> CreateValueAsync<TContext>()\n    {\n        return new ValueAsyncPipelineBuilder<TContext>(c =>\n#if NET\n            ValueTask.CompletedTask\n#else\n            default\n#endif\n        );\n    }\n    public static IValueAsyncPipelineBuilder<TContext> CreateValueAsync<TContext>(Func<TContext, ValueTask> completeFunc)\n    {\n        return new ValueAsyncPipelineBuilder<TContext>(completeFunc);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Pipelines/AsyncPipelineBuilder.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Common.Helpers;\n\npublic interface IAsyncPipelineBuilder<TContext>\n{\n    IAsyncPipelineBuilder<TContext> Use(Func<Func<TContext, Task>, Func<TContext, Task>> middleware);\n\n    Func<TContext, Task> Build();\n\n    IAsyncPipelineBuilder<TContext> New();\n}\n\ninternal sealed class AsyncPipelineBuilder<TContext>(Func<TContext, Task> completeFunc) : IAsyncPipelineBuilder<TContext>\n{\n    private readonly Func<TContext, Task> _completeFunc = completeFunc;\n    private readonly List<Func<Func<TContext, Task>, Func<TContext, Task>>> _pipelines = [];\n\n    public IAsyncPipelineBuilder<TContext> Use(Func<Func<TContext, Task>, Func<TContext, Task>> middleware)\n    {\n        _pipelines.Add(middleware);\n        return this;\n    }\n\n    public Func<TContext, Task> Build()\n    {\n        var request = _completeFunc;\n        for (var i = _pipelines.Count - 1; i >= 0; i--)\n        {\n            request = _pipelines[i](request);\n        }\n        return request;\n    }\n\n    public IAsyncPipelineBuilder<TContext> New() => new AsyncPipelineBuilder<TContext>(_completeFunc);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Pipelines/PipelineBuilder.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Common.Helpers;\n\npublic interface IPipelineBuilder<TContext>\n{\n    IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware);\n\n    Action<TContext> Build();\n\n    IPipelineBuilder<TContext> New();\n}\n\ninternal sealed class PipelineBuilder<TContext>(Action<TContext> completeFunc) : IPipelineBuilder<TContext>\n{\n    private readonly Action<TContext> _completeFunc = completeFunc;\n    private readonly List<Func<Action<TContext>, Action<TContext>>> _pipelines = [];\n\n    public IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware)\n    {\n        _pipelines.Add(middleware);\n        return this;\n    }\n\n    public Action<TContext> Build()\n    {\n        var request = _completeFunc;\n\n        for (var i = _pipelines.Count - 1; i >= 0; i--)\n        {\n            request = _pipelines[i](request);\n        }\n\n        return request;\n    }\n\n    public IPipelineBuilder<TContext> New() => new PipelineBuilder<TContext>(_completeFunc);\n}\n\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Pipelines/PipelineBuilderExtensions.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Diagnostics.CodeAnalysis;\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Common.Helpers;\n\npublic static class PipelineBuilderExtensions\n{\n    #region IPipelineBuilder\n\n    public static IPipelineBuilder<TContext> Use<TContext>(this IPipelineBuilder<TContext> builder, Action<TContext, Action> action)\n\n    {\n        return builder.Use(next =>\n            context =>\n            {\n                action(context, () => next(context));\n            });\n    }\n\n    public static IPipelineBuilder<TContext> Use<TContext>(this IPipelineBuilder<TContext> builder, Action<TContext, Action<TContext>> action)\n    {\n        return builder.Use(next =>\n            context =>\n            {\n                action(context, next);\n            });\n    }\n\n    public static IPipelineBuilder<TContext> Run<TContext>(this IPipelineBuilder<TContext> builder, Action<TContext> handler)\n    {\n        return builder.Use(_ => handler);\n    }\n\n    public static IPipelineBuilder<TContext> UseMiddleware<TContext, TMiddleware>(this IPipelineBuilder<TContext> builder, TMiddleware middleware)\n        where TMiddleware : class, IPipelineMiddleware<TContext>\n    {\n        Guard.NotNull(middleware);\n        return builder.Use(next =>\n            context =>\n            {\n                middleware.Invoke(context, next);\n            });\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IPipelineBuilder<TContext> UseMiddleware<TContext, TMiddleware>(this IPipelineBuilder<TContext> builder)\n      where TMiddleware : class, IPipelineMiddleware<TContext>\n    {\n        return builder.UseMiddleware(DependencyResolver.Current.GetServiceOrCreateInstance<TMiddleware>());\n    }\n\n    public static IPipelineBuilder<TContext> When<TContext>(this IPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IPipelineBuilder<TContext>> configureAction)\n    {\n        builder.Use((context, next) =>\n        {\n            if (predict.Invoke(context))\n            {\n                var branchPipelineBuilder = builder.New();\n                configureAction(branchPipelineBuilder);\n                var branchPipeline = branchPipelineBuilder.Build();\n                branchPipeline.Invoke(context);\n            }\n            else\n            {\n                next();\n            }\n        });\n\n        return builder;\n    }\n\n    public static IPipelineBuilder<TContext> UseWhen<TContext>(this IPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IPipelineBuilder<TContext>> configureAction)\n    {\n        var branchPipelineBuilder = builder.New();\n        configureAction(branchPipelineBuilder);\n        builder.Use((context, next) =>\n        {\n            branchPipelineBuilder.Run(_ => next(context));\n            var branch = branchPipelineBuilder.Build();\n            if (predict.Invoke(context))\n            {\n                branch(context);\n            }\n            else\n            {\n                next(context);\n            }\n        });\n\n        return builder;\n    }\n\n    #endregion IPipelineBuilder\n\n    #region IAsyncPipelineBuilder\n\n    public static IAsyncPipelineBuilder<TContext> Use<TContext>(this IAsyncPipelineBuilder<TContext> builder,\n        Func<TContext, Func<Task>, Task> func)\n    {\n        return builder.Use(next =>\n            context =>\n            {\n                return func(context, () => next(context));\n            });\n    }\n\n    public static IAsyncPipelineBuilder<TContext> Use<TContext>(this IAsyncPipelineBuilder<TContext> builder,\n        Func<TContext, Func<TContext, Task>, Task> func)\n    {\n        return builder.Use(next =>\n            context => func(context, next));\n    }\n\n    public static IAsyncPipelineBuilder<TContext> UseMiddleware<TContext>(this IAsyncPipelineBuilder<TContext> builder, IAsyncPipelineMiddleware<TContext> middleware)\n    {\n        Guard.NotNull(middleware);\n        return builder.Use(next =>\n            async context =>\n            {\n                await middleware.InvokeAsync(context, next);\n            });\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IAsyncPipelineBuilder<TContext> UseMiddleware<TContext, TMiddleware>(this IAsyncPipelineBuilder<TContext> builder)\n        where TMiddleware : class, IAsyncPipelineMiddleware<TContext>\n    {\n        return builder.UseMiddleware(DependencyResolver.Current.GetServiceOrCreateInstance<TMiddleware>());\n    }\n\n    public static IAsyncPipelineBuilder<TContext> When<TContext>(this IAsyncPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IAsyncPipelineBuilder<TContext>> configureAction)\n    {\n        builder.Use((context, next) =>\n        {\n            if (predict.Invoke(context))\n            {\n                var branchPipelineBuilder = builder.New();\n                configureAction(branchPipelineBuilder);\n                var branchPipeline = branchPipelineBuilder.Build();\n                return branchPipeline.Invoke(context);\n            }\n\n            return next();\n        });\n\n        return builder;\n    }\n\n    public static IAsyncPipelineBuilder<TContext> UseWhen<TContext>(this IAsyncPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IAsyncPipelineBuilder<TContext>> configureAction)\n    {\n        var branchPipelineBuilder = builder.New();\n        configureAction(branchPipelineBuilder);\n        builder.Use((context, next) =>\n        {\n            branchPipelineBuilder.Run(_ => next(context));\n            var branch = branchPipelineBuilder.Build();\n            if (predict.Invoke(context))\n            {\n                return branch(context);\n            }\n            return next(context);\n        });\n\n        return builder;\n    }\n\n    public static IAsyncPipelineBuilder<TContext> Run<TContext>(this IAsyncPipelineBuilder<TContext> builder, Func<TContext, Task> handler)\n    {\n        return builder.Use(_ => handler);\n    }\n    #endregion IAsyncPipelineBuilder\n\n    #region IValueAsyncPipelineBuilder\n\n    public static IValueAsyncPipelineBuilder<TContext> Use<TContext>(this IValueAsyncPipelineBuilder<TContext> builder,\n        Func<TContext, Func<ValueTask>, ValueTask> func)\n    {\n        return builder.Use(next =>\n            context =>\n            {\n                return func(context, () => next(context));\n            });\n    }\n\n    public static IValueAsyncPipelineBuilder<TContext> Use<TContext>(this IValueAsyncPipelineBuilder<TContext> builder,\n        Func<TContext, Func<TContext, ValueTask>, ValueTask> func)\n    {\n        return builder.Use(next =>\n            context => func(context, next));\n    }\n\n    public static IValueAsyncPipelineBuilder<TContext> UseMiddleware<TContext>(this IValueAsyncPipelineBuilder<TContext> builder, IValueAsyncPipelineMiddleware<TContext> middleware)\n    {\n        Guard.NotNull(middleware);\n        return builder.Use(next =>\n            async context =>\n            {\n                await middleware.InvokeAsync(context, next);\n                await next(context);\n            });\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static IValueAsyncPipelineBuilder<TContext> UseMiddleware<TContext, TMiddleware>(this IValueAsyncPipelineBuilder<TContext> builder)\n        where TMiddleware : class, IValueAsyncPipelineMiddleware<TContext>\n    {\n        return builder.UseMiddleware(DependencyResolver.Current.GetServiceOrCreateInstance<TMiddleware>());\n    }\n\n    public static IValueAsyncPipelineBuilder<TContext> When<TContext>(this IValueAsyncPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IValueAsyncPipelineBuilder<TContext>> configureAction)\n    {\n        builder.Use((context, next) =>\n        {\n            var branchPipelineBuilder = builder.New();\n            configureAction(branchPipelineBuilder);\n            if (predict.Invoke(context))\n            {\n                var branchPipeline = branchPipelineBuilder.Build();\n                return branchPipeline.Invoke(context);\n            }\n\n            return next(context);\n        });\n\n        return builder;\n    }\n\n    public static IValueAsyncPipelineBuilder<TContext> UseWhen<TContext>(this IValueAsyncPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IValueAsyncPipelineBuilder<TContext>> configureAction)\n    {\n        var branchPipelineBuilder = builder.New();\n        configureAction(branchPipelineBuilder);\n        builder.Use((context, next) =>\n        {\n            branchPipelineBuilder.Run(_ => next(context));\n            var branch = branchPipelineBuilder.Build();\n            if (predict.Invoke(context))\n            {\n                return branch(context);\n            }\n            return next(context);\n        });\n\n        return builder;\n    }\n\n    public static IValueAsyncPipelineBuilder<TContext> Run<TContext>(this IValueAsyncPipelineBuilder<TContext> builder, Func<TContext, ValueTask> handler)\n    {\n        return builder.Use(_ => handler);\n    }\n\n    #endregion IValueAsyncPipelineBuilder\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Pipelines/PipelineMiddleware.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Common.Helpers;\n\npublic interface IPipelineMiddleware<TContext>\n{\n    void Invoke(TContext context, Action<TContext> next);\n}\n\npublic interface IAsyncPipelineMiddleware<TContext>\n{\n    Task InvokeAsync(TContext context, Func<TContext, Task> next);\n}\n\npublic interface IValueAsyncPipelineMiddleware<TContext>\n{\n    ValueTask InvokeAsync(TContext context, Func<TContext, ValueTask> next);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/Pipelines/ValueAsyncPipelineBuilder.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\n// ReSharper disable once CheckNamespace\nnamespace WeihanLi.Common.Helpers;\n\npublic interface IValueAsyncPipelineBuilder<TContext>\n{\n    IValueAsyncPipelineBuilder<TContext> Use(Func<Func<TContext, ValueTask>, Func<TContext, ValueTask>> middleware);\n\n    Func<TContext, ValueTask> Build();\n\n    IValueAsyncPipelineBuilder<TContext> New();\n}\n\ninternal sealed class ValueAsyncPipelineBuilder<TContext>(Func<TContext, ValueTask> completeFunc) : IValueAsyncPipelineBuilder<TContext>\n{\n    private readonly Func<TContext, ValueTask> _completeFunc = completeFunc;\n    private readonly List<Func<Func<TContext, ValueTask>, Func<TContext, ValueTask>>> _pipelines = [];\n\n    public IValueAsyncPipelineBuilder<TContext> Use(Func<Func<TContext, ValueTask>, Func<TContext, ValueTask>> middleware)\n    {\n        _pipelines.Add(middleware);\n        return this;\n    }\n\n    public Func<TContext, ValueTask> Build()\n    {\n        var request = _completeFunc;\n        for (var i = _pipelines.Count - 1; i >= 0; i--)\n        {\n            request = _pipelines[i](request);\n        }\n        return request;\n    }\n\n    public IValueAsyncPipelineBuilder<TContext> New() => new ValueAsyncPipelineBuilder<TContext>(_completeFunc);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ProcessExecutor.cs",
    "content": "﻿using System.Diagnostics;\n\nnamespace WeihanLi.Common.Helpers;\n\n[CLSCompliant(false)]\npublic class ProcessExecutor : IDisposable\n{\n    public event EventHandler<int>? OnExited;\n\n    public event EventHandler<string>? OnOutputDataReceived;\n\n    public event EventHandler<string>? OnErrorDataReceived;\n\n    protected readonly Process _process;\n\n    protected bool _started;\n\n    public ProcessExecutor(string exePath) : this(new ProcessStartInfo(exePath))\n    {\n    }\n\n    public ProcessExecutor(string exePath, string arguments) : this(new ProcessStartInfo(exePath, arguments))\n    {\n    }\n\n    public ProcessExecutor(ProcessStartInfo startInfo)\n    {\n        _process = new Process()\n        {\n            StartInfo = startInfo,\n            EnableRaisingEvents = true,\n        };\n        _process.StartInfo.UseShellExecute = false;\n        _process.StartInfo.CreateNoWindow = true;\n        _process.StartInfo.RedirectStandardOutput = true;\n        _process.StartInfo.RedirectStandardInput = true;\n        _process.StartInfo.RedirectStandardError = true;\n    }\n\n    protected virtual void InitializeEvents()\n    {\n        _process.OutputDataReceived += (sender, args) =>\n        {\n            if (args.Data != null)\n            {\n                OnOutputDataReceived?.Invoke(sender, args.Data);\n            }\n        };\n        _process.ErrorDataReceived += (sender, args) =>\n        {\n            if (args.Data != null)\n            {\n                OnErrorDataReceived?.Invoke(sender, args.Data);\n            }\n        };\n        _process.Exited += (sender, _) =>\n        {\n            if (sender is Process process)\n            {\n                OnExited?.Invoke(sender, process.ExitCode);\n            }\n            else\n            {\n                OnExited?.Invoke(sender, _process.ExitCode);\n            }\n        };\n    }\n\n    protected virtual void Start()\n    {\n        if (_started)\n        {\n            return;\n        }\n        _started = true;\n\n        _process.Start();\n        _process.BeginOutputReadLine();\n        _process.BeginErrorReadLine();\n        _process.WaitForExit();\n    }\n\n    public virtual async Task SendInput(string input)\n    {\n        try\n        {\n            await _process.StandardInput.WriteAsync(input);\n        }\n        catch (Exception e)\n        {\n            OnErrorDataReceived?.Invoke(_process, e.ToString());\n        }\n    }\n\n    public virtual int Execute()\n    {\n        InitializeEvents();\n        Start();\n        return _process.ExitCode;\n    }\n\n    public virtual async Task<int> ExecuteAsync()\n    {\n        InitializeEvents();\n        return await Task.Run(() =>\n        {\n            Start();\n            return _process.ExitCode;\n        }).ConfigureAwait(false);\n    }\n\n    public virtual void Dispose()\n    {\n        _process.Dispose();\n        OnExited = null;\n        OnOutputDataReceived = null;\n        OnErrorDataReceived = null;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ProfilerHelper.cs",
    "content": "﻿using System.Diagnostics;\nusing WeihanLi.Common.Services;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic sealed class ProfilerStopper(IProfiler profiler, Action<TimeSpan> profileAction) : IDisposable\n{\n    private readonly IProfiler _profiler = Guard.NotNull(profiler);\n    private readonly Action<TimeSpan> _profileAction = Guard.NotNull(profileAction);\n\n    public void Dispose()\n    {\n        _profiler.Stop();\n        _profileAction(_profiler.Elapsed);\n    }\n}\n\npublic sealed class StopwatchStopper(Stopwatch stopwatch, Action<TimeSpan> profileAction) : IDisposable\n{\n    private readonly Stopwatch _stopwatch = Guard.NotNull(stopwatch);\n    private readonly Action<TimeSpan> _profileAction = Guard.NotNull(profileAction);\n\n    public void Dispose()\n    {\n        _stopwatch.Stop();\n        _profileAction(_stopwatch.Elapsed);\n    }\n}\n\npublic static class ProfilerHelper\n{\n    public static StopwatchStopper Profile(this Stopwatch watch, Action<TimeSpan> profilerAction)\n    {\n        Guard.NotNull(watch).Restart();\n        return new StopwatchStopper(watch, profilerAction);\n    }\n\n    public static ProfilerStopper StartNew(this IProfiler profiler, Action<TimeSpan> profilerAction)\n    {\n        Guard.NotNull(profiler).Restart();\n        return new ProfilerStopper(profiler, profilerAction);\n    }\n\n    public static readonly double TicksPerTimestamp = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;\n\n    /// <summary>\n    /// GetElapsedTime\n    /// </summary>\n    /// <param name=\"startTimestamp\">startTimestamp, get by Stopwatch.GetTimestamp()</param>\n    /// <returns>elapsed time</returns>\n    public static TimeSpan GetElapsedTime(long startTimestamp) =>\n#if NET\n        Stopwatch.GetElapsedTime(startTimestamp)\n#else\n        GetElapsedTime(startTimestamp, Stopwatch.GetTimestamp())\n#endif\n        ;\n\n    /// <summary>\n    /// GetElapsedTime\n    /// </summary>\n    /// <param name=\"startTimestamp\">startTimestamp, get by Stopwatch.GetTimestamp()</param>\n    /// <param name=\"endTimestamp\">endTimestamp, get by Stopwatch.GetTimestamp</param>\n    /// <returns>elapsed time</returns>\n    public static TimeSpan GetElapsedTime(long startTimestamp, long endTimestamp)\n    {\n#if NET\n        return Stopwatch.GetElapsedTime(startTimestamp, endTimestamp);\n#else\n        var ticks = (long)((endTimestamp - startTimestamp) * TicksPerTimestamp);\n        return new TimeSpan(ticks);\n#endif\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ReflectHelper.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class ReflectHelper\n{\n    public static Assembly[] GetAssemblies()\n    {\n        return AppDomain.CurrentDomain.GetAssemblies();\n    }\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public static bool IsAwaitable(this Type type)\n    {\n        Guard.NotNull(type);\n        if (type == typeof(void))\n            return false;\n\n        return AwaitableInfo.IsTypeAwaitable(type, out _);\n    }\n}\n\ninternal readonly struct AwaitableInfo(\n    Type awaiterType,\n    PropertyInfo awaiterIsCompletedProperty,\n    MethodInfo awaiterGetResultMethod,\n    MethodInfo awaiterOnCompletedMethod,\n    MethodInfo? awaiterUnsafeOnCompletedMethod,\n    Type resultType,\n    MethodInfo getAwaiterMethod)\n{\n    public Type AwaiterType { get; } = awaiterType;\n    public PropertyInfo AwaiterIsCompletedProperty { get; } = awaiterIsCompletedProperty;\n    public MethodInfo AwaiterGetResultMethod { get; } = awaiterGetResultMethod;\n    public MethodInfo AwaiterOnCompletedMethod { get; } = awaiterOnCompletedMethod;\n    public MethodInfo? AwaiterUnsafeOnCompletedMethod { get; } = awaiterUnsafeOnCompletedMethod;\n    public Type ResultType { get; } = resultType;\n    public MethodInfo GetAwaiterMethod { get; } = getAwaiterMethod;\n\n    [RequiresUnreferencedCode(\"Members from serialized types may be trimmed if not referenced directly\")]\n    public static bool IsTypeAwaitable([DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes.PublicMethods))] Type type, out AwaitableInfo? awaitableInfo)\n    {\n        // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347\n\n        // Awaitable must have method matching \"object GetAwaiter()\"\n        var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m =>\n            m.Name.Equals(\"GetAwaiter\", StringComparison.OrdinalIgnoreCase)\n            && m.GetParameters().Length == 0\n            && m.ReturnType != null);\n        if (getAwaiterMethod == null)\n        {\n            awaitableInfo = default;\n            return false;\n        }\n\n        var awaiterType = getAwaiterMethod.ReturnType;\n\n        // Awaiter must have property matching \"bool IsCompleted { get; }\"\n        var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p =>\n            p.Name.Equals(\"IsCompleted\", StringComparison.OrdinalIgnoreCase)\n            && p.PropertyType == typeof(bool)\n            && p.GetMethod != null);\n        if (isCompletedProperty == null)\n        {\n            awaitableInfo = default(AwaitableInfo);\n            return false;\n        }\n\n        // Awaiter must implement INotifyCompletion\n        var awaiterInterfaces = awaiterType.GetInterfaces();\n        var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion));\n        if (!implementsINotifyCompletion)\n        {\n            awaitableInfo = default(AwaitableInfo);\n            return false;\n        }\n\n        // INotifyCompletion supplies a method matching \"void OnCompleted(Action action)\"\n        var onCompletedMethod = typeof(INotifyCompletion).GetRuntimeMethods().Single(m =>\n            m.Name.Equals(\"OnCompleted\", StringComparison.OrdinalIgnoreCase)\n            && m.ReturnType == typeof(void)\n            && m.GetParameters().Length == 1\n            && m.GetParameters()[0].ParameterType == typeof(Action));\n\n        // Awaiter optionally implements ICriticalNotifyCompletion\n        var implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion));\n        MethodInfo? unsafeOnCompletedMethod;\n        if (implementsICriticalNotifyCompletion)\n        {\n            // ICriticalNotifyCompletion supplies a method matching \"void UnsafeOnCompleted(Action action)\"\n            unsafeOnCompletedMethod = typeof(ICriticalNotifyCompletion).GetRuntimeMethods().Single(m =>\n                m.Name.Equals(\"UnsafeOnCompleted\", StringComparison.OrdinalIgnoreCase)\n                && m.ReturnType == typeof(void)\n                && m.GetParameters().Length == 1\n                && m.GetParameters()[0].ParameterType == typeof(Action));\n        }\n        else\n        {\n            unsafeOnCompletedMethod = null;\n        }\n\n        // Awaiter must have method matching \"void GetResult\" or \"T GetResult()\"\n        var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m =>\n            m.Name.Equals(\"GetResult\")\n            && m.GetParameters().Length == 0);\n        if (getResultMethod == null)\n        {\n            awaitableInfo = default;\n            return false;\n        }\n\n        awaitableInfo = new AwaitableInfo(\n            awaiterType,\n            isCompletedProperty,\n            getResultMethod,\n            onCompletedMethod,\n            unsafeOnCompletedMethod,\n            getResultMethod.ReturnType,\n            getAwaiterMethod);\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/RequestHelper.cs",
    "content": "﻿using System.Collections.Specialized;\nusing System.Web;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class RequestHelper\n{\n    /// <summary>\n    /// Get QueryString\n    /// </summary>\n    /// <param name=\"url\">url</param>\n    /// <returns>QueryString</returns>\n    public static NameValueCollection GetParamCollection(string url)\n    {\n        return HttpUtility.ParseQueryString(url);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/RetryHelper.cs",
    "content": "﻿using Exception = System.Exception;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class RetryHelper\n{\n    #region TryInvoke\n\n    public static bool TryInvoke(Action action, int maxRetryTimes = 3, Action<int, TimeSpan, Exception?>? onRetry = null, Func<int, TimeSpan>? delayFunc = null)\n    {\n        Guard.NotNull(action, nameof(action));\n\n        var time = 0;\n        do\n        {\n            try\n            {\n                action();\n                return true;\n            }\n            catch (Exception ex)\n            {\n                time++;\n                var delay = delayFunc?.Invoke(time);\n                onRetry?.Invoke(time, delay.GetValueOrDefault(), ex);\n                if (delay.HasValue)\n                {\n                    Thread.Sleep(delay.Value);\n                }\n            }\n        } while (time <= maxRetryTimes);\n        return false;\n    }\n\n    public static bool TryInvoke(Func<bool> func, int maxRetryTimes = 3, Action<int, TimeSpan, Exception?>? onRetry = null, Func<int, TimeSpan>? delayFunc = null)\n    {\n        Guard.NotNull(func, nameof(func));\n\n        var result = false;\n        var time = 0;\n        var exception = default(Exception);\n        do\n        {\n            if (time > 0)\n            {\n                var delay = delayFunc?.Invoke(time);\n                onRetry?.Invoke(time, delay.GetValueOrDefault(), exception);\n                if (delay.HasValue)\n                {\n                    Task.Delay(delay.Value).Wait();\n                }\n            }\n            try\n            {\n                result = func();\n                exception = null;\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n                exception = ex;\n            }\n\n            time++;\n        } while (!result && time <= maxRetryTimes);\n        return result;\n    }\n\n    public static TResult? TryInvoke<TResult>(Func<TResult?> func, Func<TResult?, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null)\n    {\n        var result = default(TResult);\n        var time = 0;\n        do\n        {\n            if (time > 0 && delayFunc != null)\n            {\n                Task.Delay(delayFunc.Invoke(time)).Wait();\n            }\n            try\n            {\n                result = func();\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes);\n        return result;\n    }\n\n    public static TResult? TryInvoke<T1, TResult>(Func<T1, TResult?> func, T1 t1, Func<TResult?, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null)\n    {\n        var result = default(TResult);\n        var time = 0;\n        do\n        {\n            if (time > 0 && delayFunc != null)\n            {\n                Task.Delay(delayFunc.Invoke(time)).Wait();\n            }\n            try\n            {\n                result = func(t1);\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes);\n\n        return result;\n    }\n\n    public static TResult? TryInvoke<T1, T2, TResult>(Func<T1, T2, TResult?> func, T1 t1, T2 t2, Func<TResult?, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null)\n    {\n        var result = default(TResult);\n        var time = 0;\n        do\n        {\n            if (time > 0 && delayFunc != null)\n            {\n                Task.Delay(delayFunc.Invoke(time)).Wait();\n            }\n            try\n            {\n                result = func(t1, t2);\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes);\n        return result;\n    }\n\n    public static TResult? TryInvoke<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult?> func, T1 t1, T2 t2, T3 t3, Func<TResult?, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null)\n    {\n        var result = default(TResult);\n        var time = 0;\n        do\n        {\n            if (time > 0 && delayFunc != null)\n            {\n                Task.Delay(delayFunc.Invoke(time)).Wait();\n            }\n\n            try\n            {\n                result = func(t1, t2, t3);\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes);\n        return result;\n    }\n\n    public static TResult TryInvoke<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> func, T1 t1, T2 t2, T3 t3, T4 t4, Func<TResult, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null)\n    {\n        var result = default(TResult)!;\n        var time = 0;\n        do\n        {\n            if (time > 0 && delayFunc != null)\n            {\n                Task.Delay(delayFunc.Invoke(time)).Wait();\n            }\n            try\n            {\n                result = func(t1, t2, t3, t4);\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes);\n        return result;\n    }\n\n    #endregion TryInvoke\n\n    #region TryInvokeAsync\n\n    public static async Task<bool> TryInvokeAsync(Func<Task> action, int maxRetryTimes = 3, Action<int, TimeSpan, Exception>? onRetry = null, Func<int, TimeSpan>? delayFunc = null, CancellationToken cancellationToken = default)\n    {\n        Guard.NotNull(action, nameof(action));\n\n        var time = 0;\n        do\n        {\n            try\n            {\n                await action();\n                return true;\n            }\n            catch (Exception ex)\n            {\n                time++;\n                var delay = delayFunc?.Invoke(time);\n                onRetry?.Invoke(time, delay.GetValueOrDefault(), ex);\n                if (delay.HasValue)\n                {\n                    await Task.Delay(delay.Value, cancellationToken);\n                }\n            }\n        } while (time <= maxRetryTimes && !cancellationToken.IsCancellationRequested);\n        return false;\n    }\n\n    public static async Task<bool> TryInvokeAsync(Func<Task<bool>> func, int maxRetryTimes = 3, Action<int, TimeSpan, Exception?>? onRetry = null, Func<int, TimeSpan>? delayFunc = null, CancellationToken cancellationToken = default)\n    {\n        Guard.NotNull(func, nameof(func));\n\n        var result = false;\n        var time = 0;\n        var exception = default(Exception);\n        do\n        {\n            if (time > 0)\n            {\n                var delay = delayFunc?.Invoke(time);\n                onRetry?.Invoke(time, delay.GetValueOrDefault(), exception);\n                if (delay.HasValue)\n                {\n                    await Task.Delay(delay.Value, cancellationToken);\n                }\n            }\n            try\n            {\n                result = await func();\n                exception = null;\n            }\n            catch (Exception ex)\n            {\n                exception = ex;\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n            time++;\n        } while (!result && time <= maxRetryTimes && !cancellationToken.IsCancellationRequested);\n\n        return result;\n    }\n\n    public static async Task<TResult?> TryInvokeAsync<TResult>(Func<Task<TResult?>> func, Func<TResult?, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null, CancellationToken cancellationToken = default)\n    {\n        var result = default(TResult);\n        var time = 0;\n        do\n        {\n            if (delayFunc != null && time > 0)\n            {\n                await Task.Delay(delayFunc(time), cancellationToken);\n            }\n\n            try\n            {\n                result = await func();\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes && !cancellationToken.IsCancellationRequested);\n        return result;\n    }\n\n    public static async Task<TResult?> TryInvokeAsync<T1, TResult>(Func<T1, Task<TResult?>> func, T1 t1, Func<TResult?, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null, CancellationToken cancellationToken = default)\n    {\n        var result = default(TResult);\n        var time = 0;\n        do\n        {\n            if (delayFunc != null && time > 0)\n            {\n                await Task.Delay(delayFunc(time), cancellationToken);\n            }\n            try\n            {\n                result = await func(t1);\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes && !cancellationToken.IsCancellationRequested);\n        return result;\n    }\n\n    public static async Task<TResult?> TryInvokeAsync<T1, T2, TResult>(Func<T1, T2, Task<TResult?>> func, T1 t1, T2 t2, Func<TResult?, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null, CancellationToken cancellationToken = default)\n    {\n        var result = default(TResult);\n        var time = 0;\n        do\n        {\n            if (delayFunc != null && time > 0)\n            {\n                await Task.Delay(delayFunc(time), cancellationToken);\n            }\n            try\n            {\n                result = await func(t1, t2);\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes && !cancellationToken.IsCancellationRequested);\n        return result;\n    }\n\n    public static async Task<TResult?> TryInvokeAsync<T1, T2, T3, TResult>(Func<T1, T2, T3, Task<TResult?>> func, T1 t1, T2 t2, T3 t3, Func<TResult?, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null, CancellationToken cancellationToken = default)\n    {\n        var result = default(TResult);\n        var time = 0;\n        do\n        {\n            if (delayFunc != null && time > 0)\n            {\n                await Task.Delay(delayFunc(time), cancellationToken);\n            }\n            try\n            {\n                result = await func(t1, t2, t3);\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes && !cancellationToken.IsCancellationRequested);\n        return result;\n    }\n\n    public static async Task<TResult?> TryInvokeAsync<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, Task<TResult>> func, T1 t1, T2 t2, T3 t3, T4 t4, Func<TResult?, bool> validFunc, int maxRetryTimes = 3, Func<int, TimeSpan>? delayFunc = null, CancellationToken cancellationToken = default)\n    {\n        var result = default(TResult);\n        var time = 0;\n        do\n        {\n            if (delayFunc != null && time > 0)\n            {\n                await Task.Delay(delayFunc(time), cancellationToken);\n            }\n            try\n            {\n                result = await func(t1, t2, t3, t4);\n            }\n            catch (Exception ex)\n            {\n                InvokeHelper.OnInvokeException?.Invoke(ex);\n            }\n\n            time++;\n        } while (!validFunc(result) && time <= maxRetryTimes && !cancellationToken.IsCancellationRequested);\n        return result;\n    }\n\n    #endregion TryInvokeAsync\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/SecurityHelper.cs",
    "content": "﻿using System.Security.Cryptography;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class SecurityHelper\n{\n    private static readonly char[] _constantLetterCharacters =\n    [\n        'A',\n        'B',\n        'C',\n        'D',\n        'E',\n        'F',\n        'G',\n\n        'H',\n        'I',\n        'J',\n        'K',\n        'L',\n        'M',\n        'N',\n        'O',\n        'P',\n        'Q',\n        'R',\n        'S',\n        'T',\n        'U',\n        'V',\n        'W',\n        'X',\n        'Y',\n        'Z'\n    ];\n\n    private static readonly char[] _constantNumber =\n    [\n        '0',\n        '1',\n        '2',\n        '3',\n        '4',\n        '5',\n        '6',\n        '7',\n        '8',\n        '9'\n    ];\n\n    private static readonly char[] _constantHexNumber =\n    [\n        '0',\n        '1',\n        '2',\n        '3',\n        '4',\n        '5',\n        '6',\n        '7',\n        '8',\n        '9',\n        'A',\n        'B',\n        'C',\n        'D',\n        'E',\n        'F'\n    ];\n\n#if NET\n        public static Random Random => Random.Shared;\n#else\n\n    [ThreadStatic]\n    private static Random? _random;\n\n    /// <summary>\n    /// Thread-safe Random instance\n    /// </summary>\n    public static Random Random\n    {\n        get\n        {\n            _random ??= new();\n            return _random;\n        }\n    }\n\n#endif\n\n    /// <summary>\n    /// 生成随机验证码\n    /// </summary>\n    /// <param name=\"length\">验证码长度</param>\n    /// <param name=\"isNumberOnly\">验证码是否是纯数字</param>\n    /// <returns></returns>\n    public static string GenerateRandomCode(int length, bool isNumberOnly = false)\n    {\n        if (isNumberOnly)\n        {\n            return GenerateRandomCode(length, RandomCodeType.Number);\n        }\n\n        var charArray = new char[length];\n        var maxLength = _constantNumber.Length + _constantLetterCharacters.Length;\n        for (var i = 0; i < length; i++)\n        {\n            var idx = Random.Next(maxLength);\n            if (idx < _constantNumber.Length)\n            {\n                charArray[i] = _constantNumber[idx];\n            }\n            else\n            {\n                charArray[i] = _constantLetterCharacters[idx - _constantNumber.Length];\n            }\n        }\n\n        return new string(charArray);\n    }\n\n    public static string GenerateRandomCode(int length, RandomCodeType type)\n    {\n        if (type == RandomCodeType.LetterOrNumber) return GenerateRandomCode(length, false);\n\n        var array = type switch\n        {\n            RandomCodeType.Number => _constantNumber,\n            RandomCodeType.Letter => _constantLetterCharacters,\n            RandomCodeType.HexNumber => _constantHexNumber,\n            _ => throw new NotSupportedException($\"Type {type} is not supported\")\n        };\n        var charArray = new char[length];\n        for (var i = 0; i < length; i++)\n        {\n            charArray[i] = array[Random.Next(array.Length)];\n        }\n        return new string(charArray);\n    }\n\n    /// <summary>\n    /// get MD5 hashed string\n    /// </summary>\n    public static string MD5(string sourceString, bool isLower = false)\n    {\n        return string.IsNullOrEmpty(sourceString) ? string.Empty : HashHelper.GetHashedString(HashType.MD5, sourceString, isLower);\n    }\n\n    /// <summary>\n    /// get SHA1 hashed string\n    /// </summary>\n    public static string SHA1(string sourceString, bool isLower = false)\n    {\n        return string.IsNullOrEmpty(sourceString) ? string.Empty : HashHelper.GetHashedString(HashType.SHA1, sourceString, isLower);\n    }\n\n    /// <summary>\n    /// get SHA256 hashed string\n    /// </summary>\n    public static string SHA256(string sourceString, bool isLower = false)\n    {\n        return string.IsNullOrEmpty(sourceString) ? string.Empty : HashHelper.GetHashedString(HashType.SHA256, sourceString, isLower);\n    }\n\n    /// <summary>\n    /// get SHA512 hashed string\n    /// </summary>\n    public static string SHA512(string sourceString, bool isLower = false)\n    {\n        return string.IsNullOrEmpty(sourceString) ? string.Empty : HashHelper.GetHashedString(HashType.SHA512, sourceString, isLower);\n    }\n\n    public static string AesEncrypt(\n        string source,\n        string key,\n        string? iv = null,\n        bool isLower = false,\n        Action<Aes>? aesConfigure = null\n        )\n    {\n        using var aesAlg = Aes.Create();\n        aesAlg.Padding = PaddingMode.PKCS7;\n        if (string.IsNullOrEmpty(iv))\n        {\n            aesAlg.Mode = CipherMode.ECB;\n        }\n        else\n        {\n            aesAlg.Mode = CipherMode.CBC;\n            aesAlg.IV = iv!.GetBytes();\n        }\n\n        return aesAlg.Encrypt(source, key, isLower, aesConfigure);\n    }\n\n    public static string AesDecrypt(\n        string encryptedText,\n        string key,\n        string? iv = null,\n        Action<Aes>? aesConfigure = null\n    )\n    {\n        using var aesAlg = Aes.Create();\n        aesAlg.Padding = PaddingMode.PKCS7;\n        if (string.IsNullOrEmpty(iv))\n        {\n            aesAlg.Mode = CipherMode.ECB;\n        }\n        else\n        {\n            aesAlg.Mode = CipherMode.CBC;\n            aesAlg.IV = iv!.GetBytes();\n        }\n\n        return aesAlg.Decrypt(encryptedText, key, aesConfigure);\n    }\n\n    public static string Encrypt<TAlgorithm>(this TAlgorithm algorithm,\n        string source, string key,\n        bool isLowerCase = false,\n        Action<TAlgorithm>? algorithmConfigure = null\n        ) where TAlgorithm : SymmetricAlgorithm\n    {\n        Guard.NotNull(algorithm);\n        var encryptedBytes = Encrypt(algorithm, source.GetBytes(), key.GetBytes(), algorithmConfigure);\n        return encryptedBytes.ToHexString(isLowerCase);\n    }\n\n    public static byte[] Encrypt<TAlgorithm>(this TAlgorithm algorithm,\n        byte[] source, byte[] key, Action<TAlgorithm>? algorithmConfigure = null\n    ) where TAlgorithm : SymmetricAlgorithm\n    {\n        Guard.NotNull(algorithm);\n        algorithm.Key = key;\n        algorithmConfigure?.Invoke(algorithm);\n        using var encryptor = algorithm.CreateEncryptor();\n        return encryptor.TransformFinalBlock(source, 0, source.Length);\n    }\n\n    public static string Decrypt<TAlgorithm>(this TAlgorithm algorithm,\n        string encryptedHexString, string key,\n        Action<TAlgorithm>? algorithmConfigure = null\n    ) where TAlgorithm : SymmetricAlgorithm\n    {\n        var encryptedBytes = Decrypt(algorithm, encryptedHexString.HexStringToBytes(), key.GetBytes(), algorithmConfigure);\n        return encryptedBytes.GetString();\n    }\n\n    public static byte[] Decrypt<TAlgorithm>(this TAlgorithm algorithm,\n        byte[] encrypted, byte[] key, Action<TAlgorithm>? algorithmConfigure = null\n    ) where TAlgorithm : SymmetricAlgorithm\n    {\n        Guard.NotNull(algorithm);\n        algorithm.Key = key;\n        algorithmConfigure?.Invoke(algorithm);\n        using var transform = algorithm.CreateDecryptor();\n        return transform.TransformFinalBlock(encrypted, 0, encrypted.Length);\n    }\n}\n\n\npublic enum RandomCodeType\n{\n    Number = 0,\n    Letter = 1,\n    LetterOrNumber = 2,\n    HexNumber = 3\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/SequentialGuidGenerator.cs",
    "content": "﻿using System.Security.Cryptography;\n\nnamespace WeihanLi.Common.Helpers;\n\n/* This code is taken from https://github.com/jhtodd/SequentialGuid/blob/master/SequentialGuid/Classes/SequentialGuid.cs */\n\n/// <summary>\n/// Create Sequential Guid\n/// better performance for use guid as database primary key\n/// </summary>\npublic static class SequentialGuidGenerator\n{\n    private static readonly RandomNumberGenerator _randomNumberGenerator = RandomNumberGenerator.Create();\n\n    public static Guid Create(SequentialGuidType guidType = SequentialGuidType.SequentialAtEnd)\n    {\n        // We start with 16 bytes of cryptographically strong random data.\n        var randomBytes = new byte[10];\n        _randomNumberGenerator.GetBytes(randomBytes);\n\n        // An alternate method: use a normally-created GUID to get our initial\n        // random data:\n        // byte[] randomBytes = Guid.NewGuid().ToByteArray();\n        // This is faster than using RNGCryptoServiceProvider, but I don't\n        // recommend it because the .NET Framework makes no guarantee of the\n        // randomness of GUID data, and future versions (or different\n        // implementations like Mono) might use a different method.\n\n        // Now we have the random basis for our GUID.  Next, we need to\n        // create the six-byte block which will be our timestamp.\n\n        // We start with the number of milliseconds that have elapsed since\n        // DateTime.MinValue.  This will form the timestamp.  There's no use\n        // being more specific than milliseconds, since DateTime.Now has\n        // limited resolution.\n\n        // Using millisecond resolution for our 48-bit timestamp gives us\n        // about 5900 years before the timestamp overflows and cycles.\n        // Hopefully this should be sufficient for most purposes. :)\n        var timestamp = DateTime.UtcNow.Ticks / 10000L;\n\n        // Then get the bytes\n        var timestampBytes = BitConverter.GetBytes(timestamp);\n\n        // Since we're converting from an Int64, we have to reverse on\n        // little-endian systems.\n        if (BitConverter.IsLittleEndian)\n        {\n            Array.Reverse(timestampBytes);\n        }\n\n        var guidBytes = new byte[16];\n\n        switch (guidType)\n        {\n            case SequentialGuidType.SequentialAsString:\n            case SequentialGuidType.SequentialAsBinary:\n\n                // For string and byte-array version, we copy the timestamp first, followed\n                // by the random data.\n                Buffer.BlockCopy(timestampBytes, 2, guidBytes, 0, 6);\n                Buffer.BlockCopy(randomBytes, 0, guidBytes, 6, 10);\n\n                // If formatting as a string, we have to compensate for the fact\n                // that .NET regards the Data1 and Data2 block as an Int32 and an Int16,\n                // respectively.  That means that it switches the order on little-endian\n                // systems.  So again, we have to reverse.\n                if (guidType == SequentialGuidType.SequentialAsString && BitConverter.IsLittleEndian)\n                {\n                    Array.Reverse(guidBytes, 0, 4);\n                    Array.Reverse(guidBytes, 4, 2);\n                }\n\n                break;\n\n            case SequentialGuidType.SequentialAtEnd:\n\n                // For sequential-at-the-end versions, we copy the random data first,\n                // followed by the timestamp.\n                Buffer.BlockCopy(randomBytes, 0, guidBytes, 0, 10);\n                Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6);\n                break;\n        }\n\n        return new Guid(guidBytes);\n    }\n}\n\n/// <summary>\n/// Describes the type of a sequential GUID value.\n/// </summary>\npublic enum SequentialGuidType\n{\n    /// <summary>\n    /// The GUID should be sequential when formatted using the <see cref=\"Guid.ToString()\" /> method.\n    /// Used by MySql and PostgreSql.\n    /// </summary>\n    SequentialAsString,\n\n    /// <summary>\n    /// The GUID should be sequential when formatted using the <see cref=\"Guid.ToByteArray()\" /> method.\n    /// Used by Oracle.\n    /// </summary>\n    SequentialAsBinary,\n\n    /// <summary>\n    /// The sequential portion of the GUID should be located at the end of the Data4 block.\n    /// Used by SqlServer.\n    /// </summary>\n    SequentialAtEnd\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/StringHelper.cs",
    "content": "﻿using System.Globalization;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class StringHelper\n{\n    #region 隐藏敏感信息\n\n    /// <summary>\n    /// 敏感信息字符\n    /// </summary>\n    public const char SensitiveChar = '*';\n\n    /// <summary>\n    /// 隐藏敏感信息\n    /// </summary>\n    /// <param name=\"info\">信息实体</param>\n    /// <param name=\"left\">左边保留的字符数</param>\n    /// <param name=\"right\">右边保留的字符数</param>\n    /// <param name=\"sensitiveCharCount\">敏感字符数量</param>\n    /// <param name=\"basedOnLeft\">当长度异常时，是否显示左边 ，true显示左边，false显示右边 </param>\n    /// <returns></returns>\n    public static string HideSensitiveInfo(string? info, int left, int right, int sensitiveCharCount = 4, bool basedOnLeft = true)\n    {\n        if (string.IsNullOrEmpty(info))\n        {\n            return string.Empty;\n        }\n\n        if (right < 0) right = 0;\n        if (left < 0) left = 0;\n\n        if (info!.Length - left - right > 0)\n        {\n            return info[..left]\n                .PadRight(left + sensitiveCharCount, SensitiveChar)\n                .Insert(left + sensitiveCharCount, info[^right..]);\n        }\n\n        if (basedOnLeft)\n        {\n            return info.Length > left && left > 0\n                ? info.Remove(left).Insert(left, new string(SensitiveChar, sensitiveCharCount))\n                : info[..1].PadRight(1 + sensitiveCharCount, SensitiveChar);\n        }\n\n        return info.Length > right && right > 0\n            ? info[^right..].PadLeft(right + sensitiveCharCount, SensitiveChar)\n            : info[..1].PadLeft(1 + sensitiveCharCount, SensitiveChar);\n    }\n\n    /// <summary>\n    /// 隐藏手机号详情\n    /// </summary>\n    /// <param name=\"phone\">手机号</param>\n    /// <param name=\"left\">左边保留字符数</param>\n    /// <param name=\"right\">右边保留字符数</param>\n    /// <returns></returns>\n    public static string HideTelDetails(string? phone, int left = 3, int right = 4) => HideSensitiveInfo(phone, left, right);\n\n    /// <summary>\n    /// 隐藏邮箱地址详情\n    /// </summary>\n    /// <param name=\"email\">邮箱地址</param>\n    /// <param name=\"left\">邮箱地址头保留字符个数，默认值设置为3</param>\n    /// <returns></returns>\n    public static string HideEmailDetails(string? email, int left = 3)\n    {\n        if (string.IsNullOrEmpty(email))\n        {\n            return string.Empty;\n        }\n\n        if (email!.IsMatch(@\"\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\"))//如果是邮件地址\n        {\n            var suffixLen = email!.Length - email.LastIndexOf('@');\n            return HideSensitiveInfo(email, left, suffixLen, basedOnLeft: false);\n        }\n\n        return HideSensitiveInfo(email, left, 0);\n    }\n\n    #endregion 隐藏敏感信息\n\n    /// <summary>\n    /// 去除html标记，转换成一般文本\n    /// </summary>\n    /// <param name=\"htmlStr\">html文本</param>\n    /// <param name=\"removeWhiteSpace\">是否移除空格</param>\n    /// <returns></returns>\n    public static string Html2Text(string htmlStr, bool removeWhiteSpace = false)\n    {\n        if (string.IsNullOrEmpty(htmlStr))\n        {\n            return \"\";\n        }\n        var regEx_style = \"<style[^>]*?>[\\\\s\\\\S]*?<\\\\/style>\"; //定义style的正则表达式\n        var regEx_script = \"<script[^>]*?>[\\\\s\\\\S]*?<\\\\/script>\"; //定义script的正则表达式\n        var regEx_html = \"<[^>]+>\"; //定义HTML标签的正则表达式\n        var str = Regex.Replace(htmlStr, regEx_style, \"\");//删除css\n        str = Regex.Replace(str, regEx_script, \"\");//删除js\n        str = Regex.Replace(str, regEx_html, \"\");//删除html标记\n        str = Regex.Replace(str, \"\\t|\\r|\\n\", \" \");//去除tab、空行\n        str = str.Replace(\"&nbsp;\", \" \");\n        if (removeWhiteSpace)\n        {\n            str = htmlStr.Replace(\" \", \"\");//去除空格\n        }\n        return str.Trim();\n    }\n\n    public const string Empty = \"\";\n    public const string CarriageReturnLineFeed = \"\\r\\n\";\n    public const char CarriageReturn = '\\r';\n    public const char LineFeed = '\\n';\n    public const char Tab = '\\t';\n\n    public static string ToPascalCase(string s)\n    {\n        if (string.IsNullOrEmpty(s) || !char.IsLower(s[0]))\n        {\n            return s;\n        }\n\n        var chars = s.ToCharArray();\n\n        for (var i = 0; i < chars.Length; i++)\n        {\n            if (i == 1 && !char.IsUpper(chars[i]))\n            {\n                break;\n            }\n\n            var hasNext = (i + 1 < chars.Length);\n            if (i > 0 && hasNext && !char.IsUpper(chars[i + 1]))\n            {\n                break;\n            }\n\n            chars[i] = ToUpper(chars[i]);\n        }\n\n        return new string(chars);\n    }\n\n    // https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/StringUtils.cs\n\n    public static string ToCamelCase(string s)\n    {\n        if (string.IsNullOrEmpty(s) || !char.IsUpper(s[0]))\n        {\n            return s;\n        }\n\n        var chars = s.ToCharArray();\n\n        for (var i = 0; i < chars.Length; i++)\n        {\n            if (i == 1 && !char.IsUpper(chars[i]))\n            {\n                break;\n            }\n\n            var hasNext = (i + 1 < chars.Length);\n            if (i > 0 && hasNext && !char.IsUpper(chars[i + 1]))\n            {\n                // if the next character is a space, which is not considered uppercase\n                // (otherwise we wouldn't be here...)\n                // we want to ensure that the following:\n                // 'FOO bar' is rewritten as 'foo bar', and not as 'foO bar'\n                // The code was written in such a way that the first word in uppercase\n                // ends when if finds an uppercase letter followed by a lowercase letter.\n                // now a ' ' (space, (char)32) is considered not upper\n                // but in that case we still want our current character to become lowercase\n                if (char.IsSeparator(chars[i + 1]))\n                {\n                    chars[i] = ToLower(chars[i]);\n                }\n\n                break;\n            }\n\n            chars[i] = ToLower(chars[i]);\n        }\n\n        return new string(chars);\n    }\n\n    private static char ToLower(char c)\n    {\n        return char.ToLower(c, CultureInfo.InvariantCulture);\n    }\n\n    private static char ToUpper(char c)\n    {\n        return char.ToUpper(c, CultureInfo.InvariantCulture);\n    }\n\n    public static string ToSnakeCase(string s) => ToSeparatedCase(s, '_');\n\n    public static string ToKebabCase(string s) => ToSeparatedCase(s, '-');\n\n    private enum SeparatedCaseState\n    {\n        Start,\n        Lower,\n        Upper,\n        NewWord\n    }\n\n    private static string ToSeparatedCase(string s, char separator)\n    {\n        if (string.IsNullOrEmpty(s))\n        {\n            return s;\n        }\n\n        var sb = new StringBuilder();\n        var state = SeparatedCaseState.Start;\n\n        for (var i = 0; i < s.Length; i++)\n        {\n            if (s[i] == ' ')\n            {\n                if (state != SeparatedCaseState.Start)\n                {\n                    state = SeparatedCaseState.NewWord;\n                }\n            }\n            else if (char.IsUpper(s[i]))\n            {\n                switch (state)\n                {\n                    case SeparatedCaseState.Upper:\n                        var hasNext = (i + 1 < s.Length);\n                        if (i > 0 && hasNext)\n                        {\n                            var nextChar = s[i + 1];\n                            if (!char.IsUpper(nextChar) && nextChar != separator)\n                            {\n                                sb.Append(separator);\n                            }\n                        }\n                        break;\n\n                    case SeparatedCaseState.Lower:\n                    case SeparatedCaseState.NewWord:\n                        sb.Append(separator);\n                        break;\n                }\n\n                var c = char.ToLower(s[i], CultureInfo.InvariantCulture);\n\n                sb.Append(c);\n\n                state = SeparatedCaseState.Upper;\n            }\n            else if (s[i] == separator)\n            {\n                sb.Append(separator);\n                state = SeparatedCaseState.Start;\n            }\n            else\n            {\n                if (state == SeparatedCaseState.NewWord)\n                {\n                    sb.Append(separator);\n                }\n\n                sb.Append(s[i]);\n                state = SeparatedCaseState.Lower;\n            }\n        }\n\n        return sb.ToString();\n    }\n\n    public static bool StartsWith(this string? source, char value)\n    {\n        return !string.IsNullOrEmpty(source) && source![0] == value;\n    }\n\n    public static bool EndsWith(this string source, char value)\n    {\n        return !string.IsNullOrEmpty(source) && source[^1] == value;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/TaskHelper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class TaskHelper\n{\n    public static Task ToTask(object? obj)\n    {\n        var task = obj switch\n        {\n            ValueTask vt => vt.AsTask(),\n            Task t => t,\n            _ => Task.CompletedTask\n        };\n        return task;\n    }\n\n    public static Task<T> ToTask<T>(object? obj, T defaultValue = default!)\n    {\n        var task = obj switch\n        {\n            ValueTask<T> vt => vt.AsTask(),\n            Task<T> t => t,\n            ValueTask vt0 => vt0.AsTask().ContinueWith(_ => defaultValue),\n            Task t0 => t0.ContinueWith(_ => defaultValue),\n            T v => Task.FromResult(v),\n            _ => Task.FromResult(defaultValue)\n        };\n        return task;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/TotpHelper.cs",
    "content": "﻿using System.Text;\nusing WeihanLi.Common.Otp;\nusing WeihanLi.Common.Services;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class TotpHelper\n{\n    private static readonly Lazy<ITotpService> _totp = new(() => new TotpService(_defaultOptions!));\n\n    private static readonly TotpOptions _defaultOptions = new();\n\n    /// <summary>\n    /// Configure the default <see cref=\"TotpOptions\"/> for <see cref=\"TotpHelper\"/>\n    /// </summary>\n    /// <param name=\"configAction\">configure</param>\n    /// <returns>configured options</returns>\n    public static TotpOptions ConfigureTotpOptions(Action<TotpOptions> configAction)\n    {\n        Guard.NotNull(configAction, nameof(configAction));\n        configAction.Invoke(_defaultOptions);\n        return _defaultOptions;\n    }\n\n    /// <summary>\n    /// Generates code for the specified <paramref name=\"securityToken\"/>.\n    /// </summary>\n    /// <param name=\"securityToken\">The security token to generate code.</param>\n    /// <returns>The generated code.</returns>\n    public static string GetCode(byte[] securityToken)\n    {\n        return _totp.Value.GetCode(securityToken);\n    }\n\n    /// <summary>\n    /// Generates code for the specified <paramref name=\"securityToken\"/>.\n    /// </summary>\n    /// <param name=\"securityToken\">The security token to generate code.</param>\n    /// <returns>The generated code.</returns>\n    public static string GetCode(string securityToken) => GetCode(Encoding.UTF8.GetBytes(securityToken));\n\n    /// <summary>\n    /// Validates the code for the specified <paramref name=\"securityToken\"/>.\n    /// </summary>\n    /// <param name=\"securityToken\">The security token for verifying.</param>\n    /// <param name=\"code\">The code to validate.</param>\n    /// <returns><c>True</c> if validate succeed, otherwise, <c>false</c>.</returns>\n    public static bool VerifyCode(byte[] securityToken, string code)\n    {\n        Guard.NotNull(securityToken, nameof(securityToken));\n        return _totp.Value.VerifyCode(securityToken, code);\n    }\n\n    /// <summary>\n    /// Validates the code for the specified <paramref name=\"securityToken\"/>.\n    /// </summary>\n    /// <param name=\"securityToken\">The security token for verifying</param>\n    /// <param name=\"code\">The code to validate.</param>\n    /// <returns><c>True</c> if validate succeed, otherwise, <c>false</c>.</returns>\n    public static bool VerifyCode(string securityToken, string code) => VerifyCode(Encoding.UTF8.GetBytes(securityToken), code);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/TypeHelper.cs",
    "content": "﻿using System.Text;\n\nnamespace WeihanLi.Common.Helpers;\n\npublic static class TypeHelper\n{\n    private const char DefaultNestedTypeDelimiter = '+';\n\n    private static readonly Dictionary<Type, string> _builtInTypeNames = new()\n    {\n            { typeof(void), \"void\" },\n            { typeof(bool), \"bool\" },\n            { typeof(byte), \"byte\" },\n            { typeof(char), \"char\" },\n            { typeof(decimal), \"decimal\" },\n            { typeof(double), \"double\" },\n            { typeof(float), \"float\" },\n            { typeof(int), \"int\" },\n            { typeof(long), \"long\" },\n            { typeof(object), \"object\" },\n            { typeof(sbyte), \"sbyte\" },\n            { typeof(short), \"short\" },\n            { typeof(string), \"string\" },\n            { typeof(uint), \"uint\" },\n            { typeof(ulong), \"ulong\" },\n            { typeof(ushort), \"ushort\" }\n        };\n\n    public static string? GetTypeDisplayName(object? item, bool fullName = true)\n    {\n        return item == null ? null : GetTypeDisplayName(item.GetType(), fullName);\n    }\n\n    /// <summary>\n    /// Pretty print a type name.\n    /// </summary>\n    /// <param name=\"type\">The <see cref=\"Type\"/>.</param>\n    /// <param name=\"fullName\"><c>true</c> to print a fully qualified name.</param>\n    /// <param name=\"includeGenericParameterNames\"><c>true</c> to include generic parameter names.</param>\n    /// <param name=\"includeGenericParameters\"><c>true</c> to include generic parameters.</param>\n    /// <param name=\"nestedTypeDelimiter\">Character to use as a delimiter in nested type names</param>\n    /// <returns>The pretty printed type name.</returns>\n    public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false, bool includeGenericParameters = true, char nestedTypeDelimiter = DefaultNestedTypeDelimiter)\n    {\n        var builder = new StringBuilder();\n        ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames, includeGenericParameters, nestedTypeDelimiter));\n        return builder.ToString();\n    }\n\n    private static void ProcessType(StringBuilder builder, Type type, in DisplayNameOptions options)\n    {\n        if (type.IsGenericType)\n        {\n            var genericArguments = type.GetGenericArguments();\n            ProcessGenericType(builder, type, genericArguments, genericArguments.Length, options);\n        }\n        else if (type.IsArray)\n        {\n            ProcessArrayType(builder, type, options);\n        }\n        else if (_builtInTypeNames.TryGetValue(type, out var builtInName))\n        {\n            builder.Append(builtInName);\n        }\n        else if (type.IsGenericParameter)\n        {\n            if (options.IncludeGenericParameterNames)\n            {\n                builder.Append(type.Name);\n            }\n        }\n        else\n        {\n            var name = options.FullName ? type.FullName! : type.Name;\n            builder.Append(name);\n\n            if (options.NestedTypeDelimiter != DefaultNestedTypeDelimiter)\n            {\n                builder.Replace(DefaultNestedTypeDelimiter, options.NestedTypeDelimiter, builder.Length - name.Length, name.Length);\n            }\n        }\n    }\n\n    private static void ProcessArrayType(StringBuilder builder, Type type, in DisplayNameOptions options)\n    {\n        var innerType = type;\n        while (innerType.IsArray)\n        {\n            innerType = innerType.GetElementType()!;\n        }\n\n        ProcessType(builder, innerType, options);\n\n        while (type.IsArray)\n        {\n            builder.Append('[');\n            builder.Append(',', type.GetArrayRank() - 1);\n            builder.Append(']');\n            type = type.GetElementType()!;\n        }\n    }\n\n    private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, in DisplayNameOptions options)\n    {\n        var offset = 0;\n        if (type.IsNested)\n        {\n            offset = type.DeclaringType!.GetGenericArguments().Length;\n        }\n\n        if (options.FullName)\n        {\n            if (type.IsNested)\n            {\n                ProcessGenericType(builder, type.DeclaringType!, genericArguments, offset, options);\n                builder.Append(options.NestedTypeDelimiter);\n            }\n            else if (!string.IsNullOrEmpty(type.Namespace))\n            {\n                builder.Append(type.Namespace);\n                builder.Append('.');\n            }\n        }\n\n        var genericPartIndex = type.Name.IndexOf('`');\n        if (genericPartIndex <= 0)\n        {\n            builder.Append(type.Name);\n            return;\n        }\n\n        builder.Append(type.Name, 0, genericPartIndex);\n\n        if (options.IncludeGenericParameters)\n        {\n            builder.Append('<');\n            for (var i = offset; i < length; i++)\n            {\n                ProcessType(builder, genericArguments[i], options);\n                if (i + 1 == length)\n                {\n                    continue;\n                }\n\n                builder.Append(',');\n                if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter)\n                {\n                    builder.Append(' ');\n                }\n            }\n            builder.Append('>');\n        }\n    }\n\n    private readonly struct DisplayNameOptions(bool fullName, bool includeGenericParameterNames, bool includeGenericParameters, char nestedTypeDelimiter)\n    {\n        public bool FullName { get; } = fullName;\n\n        public bool IncludeGenericParameters { get; } = includeGenericParameters;\n\n        public bool IncludeGenericParameterNames { get; } = includeGenericParameterNames;\n\n        public char NestedTypeDelimiter { get; } = nestedTypeDelimiter;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ValidateHelper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Text.RegularExpressions;\n\nnamespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// 常用验证帮助类\n/// </summary>\npublic static class ValidateHelper\n{\n    //邮件正则表达式\n    private static readonly Regex EmailRegex = new(@\"^[a-z0-9]([a-z0-9]*[-_]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[\\.][a-z]{2,3}([\\.][a-z]{2})?$\", RegexOptions.IgnoreCase);\n\n    //手机号正则表达式\n    private static readonly Regex MobileRegex = new(\"^1[2-9][0-9]{9}$\", RegexOptions.Compiled);\n\n    /// <summary>\n    /// 是否为邮箱名\n    /// </summary>\n    public static bool IsEmail(string? s)\n    {\n        if (string.IsNullOrEmpty(s))\n        {\n            return false;\n        }\n        return EmailRegex.IsMatch(s!);\n    }\n\n    /// <summary>\n    /// 是否为手机号\n    /// </summary>\n    public static bool IsMobile(string? s)\n    {\n        if (string.IsNullOrEmpty(s))\n        {\n            return false;\n        }\n        return MobileRegex.IsMatch(s!);\n    }\n\n    /// <summary>\n    /// 是否为 IP v4 地址\n    /// </summary>\n    public static bool IsIP(string s)\n    {\n        if (string.IsNullOrEmpty(s))\n            return false;\n\n        var splits = s.Split('.');\n        if (splits.Length is not 4)\n            return false;\n\n        return splits.All(s => byte.TryParse(s, out _));\n    }\n\n    /// <summary>\n    /// 是否是身份证号\n    /// </summary>\n    public static bool IsIdCard(string id)\n    {\n        if (string.IsNullOrEmpty(id))\n        {\n            return false;\n        }\n        if (id.Length == 18)\n        {\n            return CheckIDCard18(id);\n        }\n        else if (id.Length == 15)\n        {\n            return CheckIDCard15(id);\n        }\n        return false;\n    }\n\n    /// <summary>\n    /// 是否为18位身份证号\n    /// </summary>\n    private static bool CheckIDCard18(string? id)\n    {\n        if (long.TryParse(id!.Remove(17), out var n) == false || n < Math.Pow(10, 16) || long.TryParse(id.Replace('x', '0').Replace('X', '0'), out n) == false)\n        {\n            return false;//数字验证\n        }\n\n        var address = \"11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91\";\n        if (address.IndexOf(id.Remove(2), StringComparison.OrdinalIgnoreCase) == -1)\n        {\n            return false;//省份验证\n        }\n\n        var birth = id.Substring(6, 8).Insert(6, \"-\").Insert(4, \"-\");\n        if (DateTime.TryParse(birth, out _) == false)\n            return false;//生日验证\n\n        var arrVarifyCode = (\"1,0,x,9,8,7,6,5,4,3,2\").Split(',');\n        var Wi = (\"7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2\").Split(',');\n        var Ai = id.Remove(17).ToCharArray();\n        var sum = 0;\n        for (var i = 0; i < 17; i++)\n        {\n            sum += int.Parse(Wi[i]) * int.Parse(Ai[i].ToString());\n        }\n\n        var y = sum % 11;\n        if (!arrVarifyCode[y].Equals(id.Substring(17, 1), StringComparison.CurrentCultureIgnoreCase))\n        {\n            return false;//校验码验证\n        }\n\n        return true;//符合GB11643-1999标准\n    }\n\n    /// <summary>\n    /// 是否为15位身份证号\n    /// </summary>\n    private static bool CheckIDCard15(string? id)\n    {\n        if (long.TryParse(id, out var n) == false || n < Math.Pow(10, 14))\n        {\n            return false;//数字验证\n        }\n\n        var address = \"11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91\";\n        if (address.IndexOf(id!.Remove(2), StringComparison.OrdinalIgnoreCase) == -1)\n        {\n            return false;//省份验证\n        }\n\n        var birth = id.Substring(6, 6).Insert(4, \"-\").Insert(2, \"-\");\n        if (DateTime.TryParse(birth, out _) == false)\n        {\n            return false;//生日验证\n        }\n\n        return true;//符合15位身份证标准\n    }\n\n    /// <summary>\n    /// 是否为邮政编码\n    /// </summary>\n    public static bool IsZipCode(string? s)\n    {\n        if (string.IsNullOrEmpty(s))\n        {\n            return false;\n        }\n        return s!.Length is 6 && int.TryParse(s, out _);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Helpers/ValueStopwatch.cs",
    "content": "﻿using System.Diagnostics;\n\nnamespace WeihanLi.Common.Helpers;\n\n/// <summary>\n/// Value-type replacement for <see cref=\"Stopwatch\"/> which avoids allocations.\n/// </summary>\n/// <remarks>\n/// Inspired on <seealso href=\"https://github.com/dotnet/extensions/blob/master/src/Shared/src/ValueStopwatch/ValueStopwatch.cs\"/>.\n/// </remarks>\npublic struct ValueStopwatch\n{\n    private long _startTimestamp, _stopTimestamp;\n\n    private ValueStopwatch(long startTimestamp)\n    {\n        _startTimestamp = startTimestamp;\n        _stopTimestamp = 0;\n    }\n\n    /// <summary>Gets the total elapsed time measured by the current instance.</summary>\n    /// <returns>A read-only <see cref=\"T:System.TimeSpan\"></see> representing the total elapsed time measured by the current instance.</returns>\n    public TimeSpan Elapsed\n    {\n        get\n        {\n            if (_stopTimestamp == 0)\n            {\n                _stopTimestamp = Stopwatch.GetTimestamp();\n            }\n            return ProfilerHelper.GetElapsedTime(_startTimestamp, _stopTimestamp);\n        }\n    }\n\n    /// <summary>Gets a value indicating whether the <see cref=\"ValueStopwatch\"></see> timer is running.</summary>\n    /// <returns>true if the <see cref=\"ValueStopwatch\"></see> instance is currently running and measuring elapsed time for an interval; otherwise, false.</returns>\n    public readonly bool IsRunning => _stopTimestamp == 0;\n\n    /// <summary>Stops time interval measurement, resets the elapsed time to zero, and starts measuring elapsed time.</summary>\n    public void Restart()\n    {\n        _stopTimestamp = 0;\n        _startTimestamp = Stopwatch.GetTimestamp();\n    }\n\n    /// <summary>Stops measuring elapsed time for an interval.</summary>\n    public void Stop() => _stopTimestamp = Stopwatch.GetTimestamp();\n\n    /// <summary>\n    /// Creates a new <see cref=\"ValueStopwatch\"/> that is ready to be used.\n    /// </summary>\n    public static ValueStopwatch StartNew() => new(Stopwatch.GetTimestamp());\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Http/HttpHeaderNames.cs",
    "content": "﻿namespace WeihanLi.Common.Http;\n\n/// <summary>\n/// HttpHeaderNames\n/// get latest from\n/// https://github.com/dotnet/aspnetcore/blob/main/src/Http/Headers/src/HeaderNames.cs\n/// </summary>\npublic static class HttpHeaderNames\n{\n    /// <summary>Gets the <c>Accept</c> HTTP header name.</summary>\n    public const string Accept = \"Accept\";\n\n    /// <summary>Gets the <c>Accept-Charset</c> HTTP header name.</summary>\n    public const string AcceptCharset = \"Accept-Charset\";\n\n    /// <summary>Gets the <c>Accept-Encoding</c> HTTP header name.</summary>\n    public const string AcceptEncoding = \"Accept-Encoding\";\n\n    /// <summary>Gets the <c>Accept-Language</c> HTTP header name.</summary>\n    public const string AcceptLanguage = \"Accept-Language\";\n\n    /// <summary>Gets the <c>Accept-Ranges</c> HTTP header name.</summary>\n    public const string AcceptRanges = \"Accept-Ranges\";\n\n    /// <summary>Gets the <c>Access-Control-Allow-Credentials</c> HTTP header name.</summary>\n    public const string AccessControlAllowCredentials = \"Access-Control-Allow-Credentials\";\n\n    /// <summary>Gets the <c>Access-Control-Allow-Headers</c> HTTP header name.</summary>\n    public const string AccessControlAllowHeaders = \"Access-Control-Allow-Headers\";\n\n    /// <summary>Gets the <c>Access-Control-Allow-Methods</c> HTTP header name.</summary>\n    public const string AccessControlAllowMethods = \"Access-Control-Allow-Methods\";\n\n    /// <summary>Gets the <c>Access-Control-Allow-Origin</c> HTTP header name.</summary>\n    public const string AccessControlAllowOrigin = \"Access-Control-Allow-Origin\";\n\n    /// <summary>Gets the <c>Access-Control-Expose-Headers</c> HTTP header name.</summary>\n    public const string AccessControlExposeHeaders = \"Access-Control-Expose-Headers\";\n\n    /// <summary>Gets the <c>Access-Control-Max-Age</c> HTTP header name.</summary>\n    public const string AccessControlMaxAge = \"Access-Control-Max-Age\";\n\n    /// <summary>Gets the <c>Access-Control-Request-Headers</c> HTTP header name.</summary>\n    public const string AccessControlRequestHeaders = \"Access-Control-Request-Headers\";\n\n    /// <summary>Gets the <c>Access-Control-Request-Method</c> HTTP header name.</summary>\n    public const string AccessControlRequestMethod = \"Access-Control-Request-Method\";\n\n    /// <summary>Gets the <c>Age</c> HTTP header name.</summary>\n    public const string Age = \"Age\";\n\n    /// <summary>Gets the <c>Allow</c> HTTP header name.</summary>\n    public const string Allow = \"Allow\";\n\n    /// <summary>Gets the <c>Alt-Svc</c> HTTP header name.</summary>\n    public const string AltSvc = \"Alt-Svc\";\n\n    /// <summary>Gets the <c>:authority</c> HTTP header name.</summary>\n    public const string Authority = \":authority\";\n\n    /// <summary>Gets the <c>Authorization</c> HTTP header name.</summary>\n    public const string Authorization = \"Authorization\";\n\n    /// <summary>Gets the <c>baggage</c> HTTP header name.</summary>\n    public const string Baggage = \"baggage\";\n\n    /// <summary>Gets the <c>Cache-Control</c> HTTP header name.</summary>\n    public const string CacheControl = \"Cache-Control\";\n\n    /// <summary>Gets the <c>Connection</c> HTTP header name.</summary>\n    public const string Connection = \"Connection\";\n\n    /// <summary>Gets the <c>Content-Disposition</c> HTTP header name.</summary>\n    public const string ContentDisposition = \"Content-Disposition\";\n\n    /// <summary>Gets the <c>Content-Encoding</c> HTTP header name.</summary>\n    public const string ContentEncoding = \"Content-Encoding\";\n\n    /// <summary>Gets the <c>Content-Language</c> HTTP header name.</summary>\n    public const string ContentLanguage = \"Content-Language\";\n\n    /// <summary>Gets the <c>Content-Length</c> HTTP header name.</summary>\n    public const string ContentLength = \"Content-Length\";\n\n    /// <summary>Gets the <c>Content-Location</c> HTTP header name.</summary>\n    public const string ContentLocation = \"Content-Location\";\n\n    /// <summary>Gets the <c>Content-MD5</c> HTTP header name.</summary>\n    public const string ContentMD5 = \"Content-MD5\";\n\n    /// <summary>Gets the <c>Content-Range</c> HTTP header name.</summary>\n    public const string ContentRange = \"Content-Range\";\n\n    /// <summary>Gets the <c>Content-Security-Policy</c> HTTP header name.</summary>\n    public const string ContentSecurityPolicy = \"Content-Security-Policy\";\n\n    /// <summary>Gets the <c>Content-Security-Policy-Report-Only</c> HTTP header name.</summary>\n    public const string ContentSecurityPolicyReportOnly = \"Content-Security-Policy-Report-Only\";\n\n    /// <summary>Gets the <c>Content-Type</c> HTTP header name.</summary>\n    public const string ContentType = \"Content-Type\";\n\n    /// <summary>Gets the <c>Correlation-Context</c> HTTP header name.</summary>\n    public const string CorrelationContext = \"Correlation-Context\";\n\n    /// <summary>Gets the <c>Cookie</c> HTTP header name.</summary>\n    public const string Cookie = \"Cookie\";\n\n    /// <summary>Gets the <c>Date</c> HTTP header name.</summary>\n    public const string Date = \"Date\";\n\n    /// <summary>Gets the <c>DNT</c> HTTP header name.</summary>\n    public const string DNT = \"DNT\";\n\n    /// <summary>Gets the <c>ETag</c> HTTP header name.</summary>\n    public const string ETag = \"ETag\";\n\n    /// <summary>Gets the <c>Expires</c> HTTP header name.</summary>\n    public const string Expires = \"Expires\";\n\n    /// <summary>Gets the <c>Expect</c> HTTP header name.</summary>\n    public const string Expect = \"Expect\";\n\n    /// <summary>Gets the <c>From</c> HTTP header name.</summary>\n    public const string From = \"From\";\n\n    /// <summary>Gets the <c>Grpc-Accept-Encoding</c> HTTP header name.</summary>\n    public const string GrpcAcceptEncoding = \"Grpc-Accept-Encoding\";\n\n    /// <summary>Gets the <c>Grpc-Encoding</c> HTTP header name.</summary>\n    public const string GrpcEncoding = \"Grpc-Encoding\";\n\n    /// <summary>Gets the <c>Grpc-Message</c> HTTP header name.</summary>\n    public const string GrpcMessage = \"Grpc-Message\";\n\n    /// <summary>Gets the <c>Grpc-Status</c> HTTP header name.</summary>\n    public const string GrpcStatus = \"Grpc-Status\";\n\n    /// <summary>Gets the <c>Grpc-Timeout</c> HTTP header name.</summary>\n    public const string GrpcTimeout = \"Grpc-Timeout\";\n\n    /// <summary>Gets the <c>Host</c> HTTP header name.</summary>\n    public const string Host = \"Host\";\n\n    /// <summary>Gets the <c>Keep-Alive</c> HTTP header name.</summary>\n    public const string KeepAlive = \"Keep-Alive\";\n\n    /// <summary>Gets the <c>If-Match</c> HTTP header name.</summary>\n    public const string IfMatch = \"If-Match\";\n\n    /// <summary>Gets the <c>If-Modified-Since</c> HTTP header name.</summary>\n    public const string IfModifiedSince = \"If-Modified-Since\";\n\n    /// <summary>Gets the <c>If-None-Match</c> HTTP header name.</summary>\n    public const string IfNoneMatch = \"If-None-Match\";\n\n    /// <summary>Gets the <c>If-Range</c> HTTP header name.</summary>\n    public const string IfRange = \"If-Range\";\n\n    /// <summary>Gets the <c>If-Unmodified-Since</c> HTTP header name.</summary>\n    public const string IfUnmodifiedSince = \"If-Unmodified-Since\";\n\n    /// <summary>Gets the <c>Last-Modified</c> HTTP header name.</summary>\n    public const string LastModified = \"Last-Modified\";\n\n    /// <summary>Gets the <c>Link</c> HTTP header name.</summary>\n    public const string Link = \"Link\";\n\n    /// <summary>Gets the <c>Location</c> HTTP header name.</summary>\n    public const string Location = \"Location\";\n\n    /// <summary>Gets the <c>Max-Forwards</c> HTTP header name.</summary>\n    public const string MaxForwards = \"Max-Forwards\";\n\n    /// <summary>Gets the <c>:method</c> HTTP header name.</summary>\n    public const string Method = \":method\";\n\n    /// <summary>Gets the <c>Origin</c> HTTP header name.</summary>\n    public const string Origin = \"Origin\";\n\n    /// <summary>Gets the <c>:path</c> HTTP header name.</summary>\n    public const string Path = \":path\";\n\n    /// <summary>Gets the <c>Pragma</c> HTTP header name.</summary>\n    public const string Pragma = \"Pragma\";\n\n    /// <summary>Gets the <c>Proxy-Authenticate</c> HTTP header name.</summary>\n    public const string ProxyAuthenticate = \"Proxy-Authenticate\";\n\n    /// <summary>Gets the <c>Proxy-Authorization</c> HTTP header name.</summary>\n    public const string ProxyAuthorization = \"Proxy-Authorization\";\n\n    /// <summary>Gets the <c>Proxy-Connection</c> HTTP header name.</summary>\n    public const string ProxyConnection = \"Proxy-Connection\";\n\n    /// <summary>Gets the <c>Range</c> HTTP header name.</summary>\n    public const string Range = \"Range\";\n\n    /// <summary>Gets the <c>Referer</c> HTTP header name.</summary>\n    public const string Referer = \"Referer\";\n\n    /// <summary>Gets the <c>Retry-After</c> HTTP header name.</summary>\n    public const string RetryAfter = \"Retry-After\";\n\n    /// <summary>Gets the <c>Request-Id</c> HTTP header name.</summary>\n    public const string RequestId = \"Request-Id\";\n\n    /// <summary>Gets the <c>:scheme</c> HTTP header name.</summary>\n    public const string Scheme = \":scheme\";\n\n    /// <summary>Gets the <c>Sec-WebSocket-Accept</c> HTTP header name.</summary>\n    public const string SecWebSocketAccept = \"Sec-WebSocket-Accept\";\n\n    /// <summary>Gets the <c>Sec-WebSocket-Key</c> HTTP header name.</summary>\n    public const string SecWebSocketKey = \"Sec-WebSocket-Key\";\n\n    /// <summary>Gets the <c>Sec-WebSocket-Protocol</c> HTTP header name.</summary>\n    public const string SecWebSocketProtocol = \"Sec-WebSocket-Protocol\";\n\n    /// <summary>Gets the <c>Sec-WebSocket-Version</c> HTTP header name.</summary>\n    public const string SecWebSocketVersion = \"Sec-WebSocket-Version\";\n\n    /// <summary>Gets the <c>Sec-WebSocket-Extensions</c> HTTP header name.</summary>\n    public const string SecWebSocketExtensions = \"Sec-WebSocket-Extensions\";\n\n    /// <summary>Gets the <c>Server</c> HTTP header name.</summary>\n    public const string Server = \"Server\";\n\n    /// <summary>Gets the <c>Set-Cookie</c> HTTP header name.</summary>\n    public const string SetCookie = \"Set-Cookie\";\n\n    /// <summary>Gets the <c>Strict-Transport-Security</c> HTTP header name.</summary>\n    public const string StrictTransportSecurity = \"Strict-Transport-Security\";\n\n    /// <summary>Gets the <c>TE</c> HTTP header name.</summary>\n    public const string TE = \"TE\";\n\n    /// <summary>Gets the <c>Trailer</c> HTTP header name.</summary>\n    public const string Trailer = \"Trailer\";\n\n    /// <summary>Gets the <c>Transfer-Encoding</c> HTTP header name.</summary>\n    public const string TransferEncoding = \"Transfer-Encoding\";\n\n    /// <summary>Gets the <c>Translate</c> HTTP header name.</summary>\n    public const string Translate = \"Translate\";\n\n    /// <summary>Gets the <c>traceparent</c> HTTP header name.</summary>\n    public const string TraceParent = \"traceparent\";\n\n    /// <summary>Gets the <c>tracestate</c> HTTP header name.</summary>\n    public const string TraceState = \"tracestate\";\n\n    /// <summary>Gets the <c>Upgrade</c> HTTP header name.</summary>\n    public const string Upgrade = \"Upgrade\";\n\n    /// <summary>Gets the <c>Upgrade-Insecure-Requests</c> HTTP header name.</summary>\n    public const string UpgradeInsecureRequests = \"Upgrade-Insecure-Requests\";\n\n    /// <summary>Gets the <c>User-Agent</c> HTTP header name.</summary>\n    public const string UserAgent = \"User-Agent\";\n\n    /// <summary>Gets the <c>Vary</c> HTTP header name.</summary>\n    public const string Vary = \"Vary\";\n\n    /// <summary>Gets the <c>Via</c> HTTP header name.</summary>\n    public const string Via = \"Via\";\n\n    /// <summary>Gets the <c>Warning</c> HTTP header name.</summary>\n    public const string Warning = \"Warning\";\n\n    /// <summary>Gets the <c>Sec-WebSocket-Protocol</c> HTTP header name.</summary>\n    public const string WebSocketSubProtocols = \"Sec-WebSocket-Protocol\";\n\n    /// <summary>Gets the <c>WWW-Authenticate</c> HTTP header name.</summary>\n    public const string WWWAuthenticate = \"WWW-Authenticate\";\n\n    /// <summary>Gets the <c>X-Content-Type-Options</c> HTTP header name.</summary>\n    public const string XContentTypeOptions = \"X-Content-Type-Options\";\n\n    /// <summary>Gets the <c>X-Frame-Options</c> HTTP header name.</summary>\n    public const string XFrameOptions = \"X-Frame-Options\";\n\n    /// <summary>Gets the <c>X-Powered-By</c> HTTP header name.</summary>\n    public const string XPoweredBy = \"X-Powered-By\";\n\n    /// <summary>Gets the <c>X-Requested-With</c> HTTP header name.</summary>\n    public const string XRequestedWith = \"X-Requested-With\";\n\n    /// <summary>Gets the <c>X-UA-Compatible</c> HTTP header name.</summary>\n    public const string XUACompatible = \"X-UA-Compatible\";\n\n    /// <summary>Gets the <c>X-XSS-Protection</c> HTTP header name.</summary>\n    public const string XXSSProtection = \"X-XSS-Protection\";\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Http/HttpRequester.cs",
    "content": "﻿using System.Collections.Specialized;\nusing System.Net;\nusing System.Net.Http.Headers;\nusing System.Text;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Http;\n\npublic class HttpResponse\n{\n    public HttpStatusCode StatusCode { get; set; }\n\n    public byte[]? ResponseBytes { get; set; }\n\n    public IDictionary<string, string?> Headers { get; set; } = new Dictionary<string, string?>();\n}\n\npublic class WebRequestHttpRequester : IHttpRequester\n{\n    public static IHttpRequester New() => new WebRequestHttpRequester();\n\n    #region private fields\n\n    private HttpWebRequest _request = null!;\n    private byte[]? _requestDataBytes;\n    private string _requestUrl = null!;\n\n    #endregion private fields\n\n    #region ctor\n\n    public WebRequestHttpRequester()\n    {\n    }\n\n    /// <summary>\n    /// Create HttpRequest with GET Request Method\n    /// </summary>\n    /// <param name=\"requestUrl\">requestUrl</param>\n    public WebRequestHttpRequester(string requestUrl) : this(requestUrl, HttpMethod.Get)\n    {\n    }\n\n    /// <summary>\n    /// Create HttpRequest with specific Request Method\n    /// </summary>\n    /// <param name=\"requestUrl\">requestUrl</param>\n    /// <param name=\"queryDictionary\">queryDictionary</param>\n    /// <param name=\"method\">method</param>\n    public WebRequestHttpRequester(string requestUrl, IDictionary<string, string>? queryDictionary, HttpMethod method)\n    {\n        _requestUrl = $\"{requestUrl}{(requestUrl.Contains('?') ? \"&\" : \"?\")}{queryDictionary.ToQueryString()}\";\n        _request = WebRequest.CreateHttp(requestUrl);\n        _request.UserAgent = HttpHelper.GetUserAgent();\n\n        _request.Method = method.Method;\n    }\n\n    /// <summary>\n    /// Create HttpRequest with specific Request Method\n    /// </summary>\n    /// <param name=\"requestUrl\">requestUrl</param>\n    /// <param name=\"method\">request method</param>\n    public WebRequestHttpRequester(string requestUrl, HttpMethod method)\n    {\n        _requestUrl = requestUrl;\n        _request = WebRequest.CreateHttp(requestUrl);\n        _request.UserAgent = HttpHelper.GetUserAgent();\n\n        _request.Method = method.Method;\n    }\n\n    #endregion ctor\n\n    public IHttpRequester WithUrl(string url)\n    {\n        _requestUrl = url;\n        _request = WebRequest.CreateHttp(url);\n        return this;\n    }\n\n    public IHttpRequester WithMethod(HttpMethod method)\n    {\n        _request.Method = method.Method;\n        return this;\n    }\n\n    #region AddHeader\n\n    public IHttpRequester WithHeaders(NameValueCollection customHeaders) => WithHeaders(customHeaders.ToDictionary());\n\n    public IHttpRequester WithHeaders(IEnumerable<KeyValuePair<string, string?>> customHeaders)\n    {\n        // ReSharper disable PossibleMultipleEnumeration\n        Guard.NotNull(customHeaders, nameof(customHeaders));\n        foreach (var header in customHeaders)\n        {\n            if (header.Value is null)\n            {\n                _request.Headers.Remove(header.Key);\n                continue;\n            }\n            if (header.Key.EqualsIgnoreCase(\"REFERER\"))\n            {\n                _request.Referer = header.Value;\n                continue;\n            }\n            if (header.Key.EqualsIgnoreCase(\"User-Agent\"))\n            {\n                _request.UserAgent = header.Value;\n                continue;\n            }\n            if (header.Key.EqualsIgnoreCase(\"COOKIE\"))\n            {\n                var cookieCollection = new CookieCollection();\n                var host = new Uri(_requestUrl).Host;\n                header.Value.Split(';').ForEach(c => cookieCollection.Add(new Cookie(c.Split('=')[0].Trim(), c.Split('=')[1].Trim(), \"/\", host)));\n                this.WithCookie(cookieCollection);\n                continue;\n            }\n            _request.Headers[header.Key] = header.Value;\n        }\n        return this;\n    }\n\n    #endregion AddHeader\n\n    #region private method\n\n    private static bool IsNeedRequestStream(string requestMethod) => requestMethod.EqualsIgnoreCase(\"POST\") || requestMethod.EqualsIgnoreCase(\"PUT\") || requestMethod.EqualsIgnoreCase(\"PATCH\");\n\n    #endregion private method\n\n    #region UserAgent\n\n    public IHttpRequester WithUserAgent(bool isMobile)\n    {\n        _request.UserAgent = HttpHelper.GetUserAgent(isMobile);\n        return this;\n    }\n\n    public IHttpRequester WithUserAgent(string userAgent)\n    {\n        _request.UserAgent = userAgent;\n        return this;\n    }\n\n    #endregion UserAgent\n\n    #region Referer\n\n    public IHttpRequester WithReferer(string referer)\n    {\n        _request.Referer = referer;\n        return this;\n    }\n\n    #endregion Referer\n\n    #region Proxy\n\n    public IHttpRequester WithProxy(IWebProxy proxy)\n    {\n        Guard.NotNull(proxy, nameof(proxy));\n        _request.Proxy = proxy;\n        return this;\n    }\n\n    #endregion Proxy\n\n    #region Cookie\n\n    public IHttpRequester WithCookie(string? url, Cookie cookie)\n    {\n        if (null == _request.CookieContainer)\n        {\n            _request.CookieContainer = new CookieContainer();\n        }\n\n        if (string.IsNullOrWhiteSpace(url))\n        {\n            _request.CookieContainer.Add(cookie);\n        }\n        else\n        {\n            _request.CookieContainer.Add(new Uri(url!), cookie);\n        }\n        return this;\n    }\n\n    public IHttpRequester WithCookie(string? url, CookieCollection cookies)\n    {\n        if (null == _request.CookieContainer)\n        {\n            _request.CookieContainer = new CookieContainer();\n        }\n\n        if (string.IsNullOrWhiteSpace(url))\n        {\n            _request.CookieContainer.Add(cookies);\n        }\n        else\n        {\n            _request.CookieContainer.Add(new Uri(url!), cookies);\n        }\n        return this;\n    }\n\n    #endregion Cookie\n\n    #region Parameter\n\n    public IHttpRequester WithParameters(byte[] requestBytes)\n        => WithParameters(requestBytes, null);\n\n    public IHttpRequester WithParameters(byte[] requestBytes, string? contentType)\n    {\n        Guard.NotNull(requestBytes, nameof(requestBytes));\n        _requestDataBytes = requestBytes;\n        if (string.IsNullOrWhiteSpace(contentType))\n        {\n            contentType = HttpHelper.ApplicationJsonContentType;\n        }\n        _request.ContentType = contentType;\n        return this;\n    }\n\n    #endregion Parameter\n\n    #region AddFile\n\n    public IHttpRequester WithFile(string filePath, string fileKey = \"file\",\n        IEnumerable<KeyValuePair<string, string>>? formFields = null)\n        => WithFile(Path.GetFileName(filePath), File.ReadAllBytes(filePath), fileKey, formFields);\n\n    public IHttpRequester WithFile(string fileName, byte[] fileBytes, string fileKey = \"file\",\n        IEnumerable<KeyValuePair<string, string>>? formFields = null)\n    {\n        var boundary = $\"----------------------------{DateTime.Now.Ticks:X}\";\n\n        var boundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}\\r\\n\");\n        var endBoundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}--\");\n        using (var memStream = new MemoryStream())\n        {\n            if (formFields != null)\n            {\n                foreach (var pair in formFields)\n                {\n                    memStream.Write(Encoding.UTF8.GetBytes(string.Format(HttpHelper.FormDataFormat, pair.Key, pair.Value, boundary)));\n                }\n            }\n            memStream.Write(boundaryBytes);\n            memStream.Write(Encoding.UTF8.GetBytes(string.Format(HttpHelper.FileHeaderFormat, fileKey, fileName)));\n            memStream.Write(fileBytes);\n            memStream.Write(endBoundaryBytes);\n            _requestDataBytes = memStream.ToArray();\n        }\n\n        _request.ContentType = $\"multipart/form-data; boundary={boundary}\";\n        _request.KeepAlive = true;\n\n        return this;\n    }\n\n    public IHttpRequester WithFiles(IEnumerable<string> filePaths, IEnumerable<KeyValuePair<string, string>>? formFields = null)\n        => WithFiles(\n            filePaths.Select(_ => new KeyValuePair<string, byte[]>(\n                Path.GetFileName(_),\n                File.ReadAllBytes(_))),\n            formFields);\n\n    public IHttpRequester WithFiles(IEnumerable<KeyValuePair<string, byte[]>> files,\n        IEnumerable<KeyValuePair<string, string>>? formFields = null)\n    {\n        var boundary = $\"----------------------------{DateTime.Now.Ticks:X}\";\n\n        var boundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}\\r\\n\");\n        var endBoundaryBytes = Encoding.ASCII.GetBytes($\"\\r\\n--{boundary}--\");\n\n        using (var memStream = new MemoryStream())\n        {\n            if (formFields != null)\n            {\n                foreach (var pair in formFields)\n                {\n                    memStream.Write(Encoding.UTF8.GetBytes(string.Format(HttpHelper.FormDataFormat, pair.Key, pair.Value, boundary)));\n                }\n            }\n\n            foreach (var file in files)\n            {\n                memStream.Write(boundaryBytes);\n\n                memStream.Write(Encoding.UTF8.GetBytes(string.Format(HttpHelper.FileHeaderFormat, Path.GetFileNameWithoutExtension(file.Key), file.Key)));\n                memStream.Write(file.Value);\n            }\n\n            memStream.Write(endBoundaryBytes);\n            _requestDataBytes = memStream.ToArray();\n        }\n\n        _request.ContentType = $\"multipart/form-data; boundary={boundary}\";\n        _request.KeepAlive = true;\n\n        return this;\n    }\n\n    #endregion AddFile\n\n    #region Execute\n\n    private void BuildRequest()\n    {\n        if (IsNeedRequestStream(_request.Method)\n            && _requestDataBytes != null\n            && _requestDataBytes.Length > 0)\n        {\n            _request.ContentLength = _requestDataBytes.Length;\n            var requestStream = _request.GetRequestStream();\n            requestStream.Write(_requestDataBytes);\n        }\n    }\n\n    private async Task BuildRequestAsync()\n    {\n        if (IsNeedRequestStream(_request.Method)\n            && _requestDataBytes != null\n            && _requestDataBytes.Length > 0)\n        {\n            _request.ContentLength = _requestDataBytes.Length;\n            var requestStream = await _request.GetRequestStreamAsync();\n            await requestStream.WriteAsync(_requestDataBytes);\n        }\n    }\n\n    public HttpResponse ExecuteForResponse()\n    {\n        BuildRequest();\n        var response = (HttpWebResponse)_request.GetResponse();\n        return new HttpResponse\n        {\n            Headers = response.Headers.ToDictionary(),\n            StatusCode = response.StatusCode,\n            ResponseBytes = response.ReadAllBytes()\n        };\n    }\n\n    public async Task<HttpResponse> ExecuteForResponseAsync()\n    {\n        await BuildRequestAsync();\n        var response = (HttpWebResponse)(await _request.GetResponseAsync());\n        var responseBytes = await response.ReadAllBytesAsync();\n        return new HttpResponse\n        {\n            Headers = response.Headers.ToDictionary(),\n            StatusCode = response.StatusCode,\n            ResponseBytes = responseBytes\n        };\n    }\n\n    public byte[] ExecuteBytes()\n    {\n        BuildRequest();\n        return _request.GetResponseBytesSafe();\n    }\n\n    public async Task<byte[]> ExecuteBytesAsync()\n    {\n        await BuildRequestAsync();\n        return await _request.GetResponseBytesSafeAsync();\n    }\n\n    public async Task<string> ExecuteAsync()\n    {\n        await BuildRequestAsync();\n        return await _request.GetResponseStringSafeAsync();\n    }\n\n    #endregion Execute\n}\n\npublic class HttpClientHttpRequester : IHttpRequester\n{\n    private HttpClient _client = null!;\n\n    private readonly HttpRequestMessage _request = new();\n\n    private CookieContainer? _cookieContainer;\n    private IWebProxy? _proxy;\n\n    private void BuildHttpClient()\n    {\n        var handler = new HttpClientHandler();\n        if (_proxy == null)\n        {\n            handler.UseProxy = false;\n            handler.Proxy = null;\n        }\n        else\n        {\n            handler.UseProxy = true;\n            handler.Proxy = _proxy;\n        }\n        if (_cookieContainer == null)\n        {\n            handler.UseCookies = false;\n        }\n        else\n        {\n            handler.UseCookies = true;\n            handler.CookieContainer = _cookieContainer;\n        }\n        _client = new HttpClient(handler);\n    }\n\n    public byte[] ExecuteBytes()\n    {\n        BuildHttpClient();\n        return _client.SendAsync(_request)\n            .ContinueWith(res => res.Result.Content.ReadAsByteArrayAsync().ContinueWith(r => r.Result))\n            .Result.GetAwaiter().GetResult();\n    }\n\n    public async Task<byte[]> ExecuteBytesAsync()\n    {\n        BuildHttpClient();\n        var response = await _client.SendAsync(_request);\n        var result = await response.Content.ReadAsByteArrayAsync();\n        return result;\n    }\n\n    public HttpResponse ExecuteForResponse()\n    {\n        BuildHttpClient();\n        var response = _client.SendAsync(_request).Result;\n        var responseBytes = response.Content.ReadAsByteArrayAsync().Result;\n        return new HttpResponse\n        {\n            Headers = response.Headers.ToDictionary(x => x.Key, x => x.Value?.StringJoin(\",\")),\n            ResponseBytes = responseBytes,\n            StatusCode = response.StatusCode\n        };\n    }\n\n    public async Task<HttpResponse> ExecuteForResponseAsync()\n    {\n        BuildHttpClient();\n        var response = await _client.SendAsync(_request);\n        var responseBytes = await response.Content.ReadAsByteArrayAsync();\n        return new HttpResponse\n        {\n            Headers = response.Headers.ToDictionary(x => x.Key, x => x.Value?.StringJoin(\",\")),\n            ResponseBytes = responseBytes,\n            StatusCode = response.StatusCode\n        };\n    }\n\n    public IHttpRequester WithCookie(string? url, Cookie cookie)\n    {\n        _cookieContainer ??= new CookieContainer();\n\n        if (string.IsNullOrEmpty(url))\n        {\n            _cookieContainer.Add(cookie);\n        }\n        else\n        {\n            _cookieContainer.Add(new Uri(url!), cookie);\n        }\n        return this;\n    }\n\n    public IHttpRequester WithCookie(string? url, CookieCollection cookies)\n    {\n        _cookieContainer ??= new CookieContainer();\n        if (string.IsNullOrWhiteSpace(url))\n        {\n            _cookieContainer.Add(cookies);\n        }\n        else\n        {\n            _cookieContainer.Add(new Uri(url!), cookies);\n        }\n        return this;\n    }\n\n    public IHttpRequester WithProxy(IWebProxy proxy)\n    {\n        _proxy = proxy;\n        return this;\n    }\n\n    public IHttpRequester WithFile(string fileName, byte[] fileBytes, string fileKey = \"file\", IEnumerable<KeyValuePair<string, string>>? formFields = null)\n    {\n        var content = new MultipartFormDataContent($\"form--{DateTime.Now.Ticks:X}\");\n        if (formFields != null)\n        {\n            foreach (var kv in formFields)\n            {\n                content.Add(new StringContent(kv.Value), kv.Key);\n            }\n        }\n        content.Add(new ByteArrayContent(fileBytes), fileKey, fileName);\n\n        _request.Content = content;\n        return this;\n    }\n\n    public IHttpRequester WithFiles(IEnumerable<KeyValuePair<string, byte[]>> files, IEnumerable<KeyValuePair<string, string>>? formFields = null)\n    {\n        var content = new MultipartFormDataContent($\"form--{DateTime.Now.Ticks:X}\");\n        if (formFields != null)\n        {\n            foreach (var kv in formFields)\n            {\n                content.Add(new StringContent(kv.Value), kv.Key);\n            }\n        }\n\n        files.ForEach((file, idx) => { content.Add(new ByteArrayContent(file.Value), $\"{file.Key}_{idx}\", file.Key); });\n\n        _request.Content = content;\n        return this;\n    }\n\n    public IHttpRequester WithUrl(string url)\n    {\n        _request.RequestUri = new Uri(url);\n        return this;\n    }\n\n    public IHttpRequester WithMethod(HttpMethod method)\n    {\n        _request.Method = method;\n        return this;\n    }\n\n    public IHttpRequester WithHeaders(IEnumerable<KeyValuePair<string, string?>>? customHeaders)\n    {\n        if (customHeaders != null)\n        {\n            foreach (var header in customHeaders)\n            {\n                // The System.Net.Http APIs require HttpRequestMessage headers to be properly divided between the request headers\n                // collection and the request content headers collection for all well-known header names.  And custom headers\n                // are only allowed in the request headers collection and not in the request content headers collection.\n                if (HttpHelper.IsWellKnownContentHeader(header.Key))\n                {\n                    _request.Content ??= new ByteArrayContent([]);\n                    if (header.Value is null)\n                    {\n                        _request.Content.Headers.Remove(header.Key);\n                    }\n                    else\n                    {\n                        _request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);\n                    }\n                }\n                else\n                {\n                    if (header.Value is null)\n                    {\n                        _request.Headers.Remove(header.Key);\n                    }\n                    else\n                    {\n                        _request.Headers.TryAddWithoutValidation(header.Key, header.Value);\n                    }\n                }\n            }\n        }\n\n        return this;\n    }\n\n    public IHttpRequester WithParameters(byte[] requestBytes, string contentType)\n    {\n        _request.Content = new ByteArrayContent(requestBytes);\n        _request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);\n        return WithHeaders(new Dictionary<string, string?>\n            {\n                {HttpHeaderNames.ContentType, contentType}\n            });\n    }\n\n    public IHttpRequester WithReferer(string referer)\n    {\n        _request.Headers.Referrer = new Uri(referer);\n        _request.Headers.TryAddWithoutValidation(HttpHeaderNames.Referer, referer);\n        return this;\n    }\n\n    public IHttpRequester WithUserAgent(string userAgent)\n    {\n        _request.Headers.TryAddWithoutValidation(HttpHeaderNames.UserAgent, userAgent);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Http/IHttpRequester.cs",
    "content": "﻿using System.Net;\n\nnamespace WeihanLi.Common.Http;\n\npublic interface IHttpRequester\n{\n    #region WithUrl\n\n    IHttpRequester WithUrl(string url);\n\n    #endregion WithUrl\n\n    #region Method\n\n    IHttpRequester WithMethod(HttpMethod method);\n\n    #endregion Method\n\n    #region AddHeader\n\n    IHttpRequester WithHeaders(IEnumerable<KeyValuePair<string, string?>> customHeaders);\n\n    #endregion AddHeader\n\n    #region UserAgent\n\n    IHttpRequester WithUserAgent(string userAgent);\n\n    #endregion UserAgent\n\n    #region Referer\n\n    IHttpRequester WithReferer(string referer);\n\n    #endregion Referer\n\n    #region Cookie\n\n    IHttpRequester WithCookie(string? url, Cookie cookie);\n\n    IHttpRequester WithCookie(string? url, CookieCollection cookies);\n\n    #endregion Cookie\n\n    #region Proxy\n\n    IHttpRequester WithProxy(IWebProxy proxy);\n\n    #endregion Proxy\n\n    #region Parameter\n\n    IHttpRequester WithParameters(byte[] requestBytes, string contentType);\n\n    IHttpRequester WithFile(string fileName, byte[] fileBytes, string fileKey = \"file\",\n        IEnumerable<KeyValuePair<string, string>>? formFields = null);\n\n    IHttpRequester WithFiles(IEnumerable<KeyValuePair<string, byte[]>> files,\n        IEnumerable<KeyValuePair<string, string>>? formFields = null);\n\n    #endregion Parameter\n\n    #region Execute\n\n    byte[] ExecuteBytes();\n\n    Task<byte[]> ExecuteBytesAsync();\n\n    HttpResponse ExecuteForResponse();\n\n    Task<HttpResponse> ExecuteForResponseAsync();\n\n    #endregion Execute\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Http/JsonHttpContent.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the MIT license.\n\nusing Newtonsoft.Json;\nusing System.Text;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Http;\n\npublic sealed class JsonHttpContent(string content, Encoding encoding) : StringContent(content, encoding, HttpHelper.ApplicationJsonMediaType)\n{\n    public JsonHttpContent(object obj, JsonSerializerSettings? jsonSerializerSettings = null)\n        : this(JsonConvert.SerializeObject(obj, jsonSerializerSettings))\n    {\n    }\n\n    public JsonHttpContent(string content) : this(content, Encoding.UTF8)\n    {\n    }\n\n    public static HttpContent From(object? obj, JsonSerializerSettings? serializerSettings = null)\n    {\n        if (obj is null)\n        {\n            return new StringContent(string.Empty, Encoding.UTF8, HttpHelper.ApplicationJsonMediaType);\n        }\n        return new JsonHttpContent(obj, serializerSettings);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Http/MockHttpHandler.cs",
    "content": "﻿using System.Net;\n\nnamespace WeihanLi.Common.Http;\n\npublic sealed class MockHttpHandler : HttpMessageHandler\n{\n    private Func<HttpRequestMessage, Task<HttpResponseMessage>> _responseFactory;\n\n    public MockHttpHandler() : this(HttpStatusCode.OK)\n    {\n    }\n\n    public MockHttpHandler(HttpStatusCode httpStatusCode) : this(_ => Task.FromResult(new HttpResponseMessage(httpStatusCode)))\n    {\n    }\n\n    public MockHttpHandler(Func<HttpRequestMessage, HttpResponseMessage> responseFactory)\n    {\n        Guard.NotNull(responseFactory);\n        _responseFactory = req => Task.FromResult(responseFactory(req));\n    }\n\n    public MockHttpHandler(Func<HttpRequestMessage, Task<HttpResponseMessage>> responseFactory)\n    {\n        Guard.NotNull(responseFactory);\n        _responseFactory = responseFactory;\n    }\n\n    public void SetResponseFactory(Func<HttpRequestMessage, HttpResponseMessage> responseFactory)\n    {\n        Guard.NotNull(responseFactory);\n        _responseFactory = req => Task.FromResult(responseFactory(req));\n    }\n\n    public void SetResponseFactory(Func<HttpRequestMessage, Task<HttpResponseMessage>> responseFactory)\n    {\n        Guard.NotNull(responseFactory);\n        _responseFactory = responseFactory;\n    }\n\n    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\n    {\n        return _responseFactory(request);\n    }\n\n    public HttpClient GetHttpClient() => new(this)\n    {\n        BaseAddress = new Uri(\"http://localhost/\")\n    };\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Http/NoProxyHttpClientHandler.cs",
    "content": "﻿namespace WeihanLi.Common.Http;\n\npublic sealed class NoProxyHttpClientHandler : HttpClientHandler\n{\n    public NoProxyHttpClientHandler()\n    {\n        Proxy = null;\n        UseProxy = false;\n        UseCookies = false;\n        AllowAutoRedirect = false;\n        CheckCertificateRevocationList = false;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/IDependencyResolver.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common;\n\n/// <inheritdoc />\n/// <summary>\n/// IDependencyResolver\n/// </summary>\npublic interface IDependencyResolver : IServiceProvider\n{\n    /// <summary>\n    /// GetServices\n    /// </summary>\n    /// <returns></returns>\n    IEnumerable<object> GetServices(Type serviceType);\n\n    /// <summary>\n    /// Invoke action via get a service instance internal\n    /// </summary>\n    /// <typeparam name=\"TService\">service type</typeparam>\n    /// <param name=\"action\">action</param>\n    bool TryInvokeService<TService>(Action<TService> action);\n\n    Task<bool> TryInvokeServiceAsync<TService>(Func<TService, Task> action);\n}\n\n/// <summary>\n/// DependencyResolverExtensions\n/// </summary>\npublic static class DependencyResolverExtensions\n{\n    /// <summary>\n    /// TryGetService\n    /// </summary>\n    /// <param name=\"dependencyResolver\">dependencyResolver</param>\n    /// <param name=\"serviceType\">serviceType</param>\n    /// <param name=\"service\">service</param>\n    /// <returns>true if successfully get service otherwise false</returns>\n    public static bool TryGetService(this IDependencyResolver dependencyResolver, Type serviceType, out object? service)\n    {\n        try\n        {\n            service = dependencyResolver.GetService(serviceType);\n            return service != null;\n        }\n        catch (Exception e)\n        {\n            service = null;\n            InvokeHelper.OnInvokeException?.Invoke(e);\n            return false;\n        }\n    }\n\n    public static bool TryResolveService<TService>(this IDependencyResolver dependencyResolver,\n        out TService? service)\n    {\n        var result = dependencyResolver.TryGetService(typeof(TService), out var serviceObj);\n        if (result)\n        {\n            service = (TService)serviceObj!;\n        }\n        else\n        {\n            service = default;\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Json/DateTimeFormatConverter.cs",
    "content": "﻿using Newtonsoft.Json.Converters;\n\nnamespace WeihanLi.Common.Json;\n\npublic class DateTimeFormatConverter : IsoDateTimeConverter\n{\n    public DateTimeFormatConverter(string format)\n    {\n        DateTimeFormat = format;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Json/IPAddressConverter.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing System.Net;\n\nnamespace WeihanLi.Common.Json;\n\n// https://stackoverflow.com/questions/18668617/json-net-error-getting-value-from-scopeid-on-system-net-ipaddress\n/// <summary>\n/// IPAddress JsonConverter\n/// </summary>\n// ReSharper disable once InconsistentNaming\npublic class IPAddressConverter : JsonConverter\n{\n    public override bool CanConvert(Type objectType)\n    {\n        return objectType == typeof(IPAddress);\n    }\n\n    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)\n    {\n        if (value == null)\n        {\n            writer.WriteNull();\n        }\n        else\n        {\n            writer.WriteValue(value.ToString());\n        }\n    }\n\n    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)\n    {\n        if (reader.Value == null)\n        {\n            return null;\n        }\n        return IPAddress.Parse((string)reader.Value);\n    }\n}\n\n/// <summary>\n/// IpEndPoint JsonConverter\n/// </summary>\n// ReSharper disable once InconsistentNaming\npublic class IPEndPointConverter : JsonConverter\n{\n    public override bool CanConvert(Type objectType)\n    {\n        return (objectType == typeof(IPEndPoint));\n    }\n\n    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)\n    {\n        var endPoint = (IPEndPoint?)value;\n        if (endPoint == null)\n        {\n            writer.WriteNull();\n        }\n        else\n        {\n            var obj = new JObject\n                {\n                    { \"Address\", JToken.FromObject(endPoint.Address, serializer) },\n                    { \"Port\", endPoint.Port }\n                };\n            obj.WriteTo(writer);\n        }\n    }\n\n    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)\n    {\n        var jObject = JObject.Load(reader);\n        if (jObject == null) return null;\n\n        var address = jObject[\"Address\"]?.ToObject<IPAddress>(serializer);\n        var port = jObject[\"Port\"]?.Value<int>() ?? 0;\n        return new IPEndPoint(Guard.NotNull(address, nameof(address)), port);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/ConsoleLoggingProvider.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing System.Collections.Concurrent;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Logging;\n\npublic interface IConsoleLogFormatter\n{\n    string FormatAsString(LogHelperLoggingEvent loggingEvent);\n}\n\ninternal sealed class DefaultConsoleLogFormatter : IConsoleLogFormatter\n{\n    private static readonly JsonSerializerSettings _serializerSettings = new()\n    {\n        Converters =\n            {\n                new StringEnumConverter()\n            },\n        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\n    };\n\n    public string FormatAsString(LogHelperLoggingEvent loggingEvent)\n    {\n        return loggingEvent.ToJson(_serializerSettings);\n    }\n}\n\ninternal sealed class DelegateConsoleLogFormatter(Func<LogHelperLoggingEvent, string> formatter) : IConsoleLogFormatter\n{\n    private readonly Func<LogHelperLoggingEvent, string> _formatter = Guard.NotNull(formatter);\n\n    public string FormatAsString(LogHelperLoggingEvent loggingEvent) => _formatter(loggingEvent);\n}\n\ninternal sealed class ConsoleLoggingProvider : ILogHelperProvider\n{\n    private readonly IConsoleLogFormatter _formatter;\n\n    private readonly BlockingCollection<LogHelperLoggingEvent> _messageQueue = [];\n    private readonly Thread _outputThread;\n\n    public ConsoleLoggingProvider(IConsoleLogFormatter formatter)\n    {\n        _formatter = formatter;\n\n        // Start Console message queue processor\n        _outputThread = new Thread(ProcessLogQueue)\n        {\n            IsBackground = true,\n            Name = \"Console logger queue processing thread\"\n        };\n        _outputThread.Start();\n    }\n\n    public void EnqueueMessage(LogHelperLoggingEvent message)\n    {\n        if (!_messageQueue.IsAddingCompleted)\n        {\n            try\n            {\n                _messageQueue.Add(message);\n                return;\n            }\n            catch (InvalidOperationException) { }\n        }\n\n        // Adding is completed so just log the message\n        try\n        {\n            WriteLoggingEvent(message);\n        }\n        catch (Exception)\n        {\n            // ignored\n        }\n    }\n\n    public void Log(LogHelperLoggingEvent loggingEvent)\n    {\n        EnqueueMessage(loggingEvent);\n    }\n\n    private void ProcessLogQueue()\n    {\n        try\n        {\n            foreach (var message in _messageQueue.GetConsumingEnumerable())\n            {\n                WriteLoggingEvent(message);\n            }\n        }\n        catch\n        {\n            try\n            {\n                _messageQueue.CompleteAdding();\n            }\n            catch\n            {\n                // ignored\n            }\n        }\n    }\n\n    private void WriteLoggingEvent(LogHelperLoggingEvent loggingEvent)\n    {\n        try\n        {\n            var logLevelColor = GetLogLevelConsoleColor(loggingEvent.LogLevel).GetValueOrDefault(Console.ForegroundColor);\n            ConsoleHelper.InvokeWithConsoleColor(\n                () =>\n                {\n                    try\n                    {\n                        var log = _formatter.FormatAsString(loggingEvent);\n                        if (loggingEvent.LogLevel is LogHelperLogLevel.Error\n                            or LogHelperLogLevel.Fatal)\n                        {\n                            Console.Error.WriteLine(log);\n                        }\n                        else\n                        {\n                            Console.Out.WriteLine(log);\n                        }\n                    }\n                    catch (Exception ex)\n                    {\n                        Console.WriteLine(ex);\n                    }\n                }, logLevelColor);\n        }\n        catch\n        {\n            Console.WriteLine(@\"Error when trying to log to console\" + loggingEvent.ToJson());\n        }\n    }\n\n    private static ConsoleColor? GetLogLevelConsoleColor(LogHelperLogLevel logLevel)\n    {\n        return logLevel switch\n        {\n            LogHelperLogLevel.Trace => ConsoleColor.Gray,\n            LogHelperLogLevel.Debug => ConsoleColor.DarkGray,\n            LogHelperLogLevel.Info => ConsoleColor.DarkGreen,\n            LogHelperLogLevel.Warn => ConsoleColor.Yellow,\n            LogHelperLogLevel.Error => ConsoleColor.Red,\n            LogHelperLogLevel.Fatal => ConsoleColor.DarkRed,\n            _ => null\n        };\n    }\n}\n\npublic static class ConsoleLoggingProviderExtensions\n{\n    public static ILogHelperLoggingBuilder AddConsole(this ILogHelperLoggingBuilder loggingBuilder, IConsoleLogFormatter? consoleLogFormatter = null)\n    {\n        loggingBuilder.AddProvider(new ConsoleLoggingProvider(\n            consoleLogFormatter ?? new DefaultConsoleLogFormatter()));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder AddConsole(this ILogHelperLoggingBuilder loggingBuilder, Func<LogHelperLoggingEvent, string> formatter)\n    {\n        loggingBuilder.AddProvider(new ConsoleLoggingProvider(new DelegateConsoleLogFormatter(formatter)));\n        return loggingBuilder;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/FileLoggingProcessor.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Logging;\nusing System.Collections.Concurrent;\nusing System.Text;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Logging;\n\ninternal sealed class FileLoggingProcessor : DisposableBase\n{\n    private readonly FileLoggingOptions _options;\n    private readonly BlockingCollection<(string log, DateTimeOffset timestamp)> _messageQueue = [];\n    private readonly Thread _outputThread;\n\n    private FileStream? _fileStream;\n    private string? _logFileName;\n\n    public FileLoggingProcessor(FileLoggingOptions options)\n    {\n        if (!Directory.Exists(options.LogsDirectory))\n        {\n            try\n            {\n                Directory.CreateDirectory(options.LogsDirectory);\n            }\n            catch (Exception e)\n            {\n                throw new InvalidOperationException(\"Failed to create log directory\", e);\n            }\n        }\n\n        _options = options;\n        _outputThread = new Thread(ProcessLogQueue)\n        {\n            IsBackground = true,\n            Priority = ThreadPriority.BelowNormal,\n            Name = \"FileLoggingProcessor\"\n        };\n        _outputThread.Start();\n    }\n\n    public void EnqueueLog(string log, DateTimeOffset dateTimeOffset)\n    {\n        if (_messageQueue.IsAddingCompleted) return;\n\n        try\n        {\n            _messageQueue.Add((log, dateTimeOffset));\n        }\n        catch (InvalidOperationException) { }\n    }\n\n    protected override void Dispose(bool disposing)\n    {\n        if (!disposing) return;\n\n        _messageQueue.CompleteAdding();\n        _fileStream?.Flush();\n        _fileStream?.Dispose();\n        _messageQueue.Dispose();\n    }\n\n    private void ProcessLogQueue()\n    {\n        try\n        {\n            foreach (var message in _messageQueue.GetConsumingEnumerable())\n            {\n                WriteLoggingEvent(message.log, message.timestamp);\n            }\n        }\n        catch\n        {\n            try\n            {\n                _messageQueue.CompleteAdding();\n            }\n            catch\n            {\n                // ignored\n            }\n        }\n    }\n\n    private void WriteLoggingEvent(string log, DateTimeOffset timestamp)\n    {\n        var fileName = _options.FileFormat.Replace(\"{date}\", timestamp.ToString(\"yyyyMMdd\"));\n        var fileInfo = new FileInfo(Path.Combine(_options.LogsDirectory, fileName));\n\n        try\n        {\n            var previousFileName = Interlocked.CompareExchange(ref _logFileName, fileInfo.FullName, _logFileName);\n            if (_logFileName != previousFileName)\n            {\n                // file name changed\n                var fs = File.OpenWrite(fileInfo.FullName);\n                var originalWriter = Interlocked.Exchange(ref _fileStream, fs);\n                if (originalWriter is not null)\n                {\n                    originalWriter.Flush();\n                    originalWriter.Dispose();\n                }\n            }\n\n            Guard.NotNull(_fileStream);\n            var bytes = Encoding.UTF8.GetBytes(log);\n            _fileStream.Write(bytes, 0, bytes.Length);\n            _fileStream.Flush();\n        }\n        catch (Exception ex)\n        {\n            Console.WriteLine($@\"Error when trying to log to file({fileInfo.FullName}) \\n\" + log + Environment.NewLine + ex);\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LogHelperExtensions.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Logging;\n\n/// <summary>\n/// LogHelperExtensions\n/// </summary>\npublic static class LogHelperExtensions\n{\n    public static void Log(this ILogHelperLogger logger, LogHelperLogLevel loggerLevel, string? msg) => logger.Log(loggerLevel, null, msg);\n\n    #region Info\n\n    public static void Info(this ILogHelperLogger logger, string? msg, params object[] parameters)\n    {\n        logger.Log(LogHelperLogLevel.Info, null, msg, parameters);\n    }\n\n    public static void Info(this ILogHelperLogger logger, Exception? ex, string? msg) => logger.Log(LogHelperLogLevel.Info, ex, msg);\n\n    public static void Info(this ILogHelperLogger logger, Exception? ex) => logger.Log(LogHelperLogLevel.Info, ex, ex?.Message);\n\n    #endregion Info\n\n    #region Trace\n\n    public static void Trace(this ILogHelperLogger logger, string? msg, params object[] parameters)\n    {\n        logger.Log(LogHelperLogLevel.Trace, null, msg, parameters);\n    }\n\n    public static void Trace(this ILogHelperLogger logger, Exception? ex, string? msg) => logger.Log(LogHelperLogLevel.Trace, ex, msg);\n\n    public static void Trace(this ILogHelperLogger logger, Exception? ex) => logger.Log(LogHelperLogLevel.Trace, ex, ex?.Message);\n\n    #endregion Trace\n\n    #region Debug\n\n    public static void Debug(this ILogHelperLogger logger, string? msg, params object[] parameters)\n    {\n        logger.Log(LogHelperLogLevel.Debug, null, msg, parameters);\n    }\n\n    public static void Debug(this ILogHelperLogger logger, Exception? ex, string? msg) => logger.Log(LogHelperLogLevel.Debug, ex, msg);\n\n    public static void Debug(this ILogHelperLogger logger, Exception? ex) => logger.Log(LogHelperLogLevel.Debug, ex, ex?.Message);\n\n    #endregion Debug\n\n    #region Warn\n\n    public static void Warn(this ILogHelperLogger logger, string? msg, params object[] parameters)\n    {\n        logger.Log(LogHelperLogLevel.Warn, null, msg, parameters);\n    }\n\n    public static void Warn(this ILogHelperLogger logger, Exception? ex, string? msg) => logger.Log(LogHelperLogLevel.Warn, ex, msg);\n\n    public static void Warn(this ILogHelperLogger logger, Exception? ex) => logger.Log(LogHelperLogLevel.Warn, ex, ex?.Message);\n\n    #endregion Warn\n\n    #region Error\n\n    public static void Error(this ILogHelperLogger logger, string? msg, params object[] parameters)\n    {\n        logger.Log(LogHelperLogLevel.Error, null, msg, parameters);\n    }\n\n    public static void Error(this ILogHelperLogger logger, Exception? ex, string? msg) => logger.Log(LogHelperLogLevel.Error, ex, msg);\n\n    public static void Error(this ILogHelperLogger logger, Exception? ex) => logger.Log(LogHelperLogLevel.Error, ex, ex?.Message);\n\n    #endregion Error\n\n    #region Fatal\n\n    public static void Fatal(this ILogHelperLogger logger, string? msg, params object[] parameters)\n    {\n        logger.Log(LogHelperLogLevel.Fatal, null, msg, parameters);\n    }\n\n    public static void Fatal(this ILogHelperLogger logger, Exception? ex, string? msg) => logger.Log(LogHelperLogLevel.Fatal, ex, msg);\n\n    public static void Fatal(this ILogHelperLogger logger, Exception? ex) => logger.Log(LogHelperLogLevel.Fatal, ex, ex?.Message);\n\n    #endregion Fatal\n\n    #region ILogHelperFactory\n\n    public static ILogHelperLogger GetLogger<T>(this ILogHelperFactory logHelperFactory) =>\n        GetLogger(logHelperFactory, typeof(T));\n\n    public static ILogHelperLogger GetLogger(this ILogHelperFactory logHelperFactory, Type type)\n    {\n        Guard.NotNull(logHelperFactory, nameof(logHelperFactory));\n\n        return logHelperFactory.CreateLogger(type.FullName ?? type.Name);\n    }\n\n    #endregion ILogHelperFactory\n\n    #region ILogHelperLoggingBuilder\n\n    public static ILogHelperLoggingBuilder WithMinimumLevel(this ILogHelperLoggingBuilder loggingBuilder, LogHelperLogLevel logLevel)\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        return loggingBuilder.WithFilter(level => level >= logLevel);\n    }\n\n    public static ILogHelperLoggingBuilder WithFilter(this ILogHelperLoggingBuilder loggingBuilder, Func<LogHelperLogLevel, bool> filterFunc)\n    {\n        loggingBuilder.AddFilter((_, e) => filterFunc.Invoke(e.LogLevel));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithFilter(this ILogHelperLoggingBuilder loggingBuilder, Func<string, LogHelperLogLevel, bool> filterFunc)\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddFilter((_, e) => filterFunc.Invoke(e.CategoryName, e.LogLevel));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithFilter(this ILogHelperLoggingBuilder loggingBuilder, Func<Type, string, LogHelperLogLevel, bool> filterFunc)\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddFilter((type, e) => filterFunc.Invoke(type, e.CategoryName, e.LogLevel));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithFilter(this ILogHelperLoggingBuilder loggingBuilder, Func<Type, string, LogHelperLogLevel, Exception?, bool> filterFunc)\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddFilter((type, e) => filterFunc.Invoke(type, e.CategoryName, e.LogLevel, e.Exception));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithFilter(this ILogHelperLoggingBuilder loggingBuilder, Func<Type, LogHelperLoggingEvent, bool> filterFunc)\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddFilter(filterFunc);\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithProvider(this ILogHelperLoggingBuilder loggingBuilder, ILogHelperProvider logHelperProvider)\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddProvider(logHelperProvider);\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithProvider<TLogProvider>(this ILogHelperLoggingBuilder loggingBuilder) where TLogProvider : ILogHelperProvider, new()\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddProvider(new TLogProvider());\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithProvider<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TLogProvider>(this ILogHelperLoggingBuilder loggingBuilder, params object[] ctorParams) where TLogProvider : ILogHelperProvider\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddProvider(ActivatorHelper.CreateInstance<TLogProvider>(ctorParams));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithEnricher<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TEnricher>(this ILogHelperLoggingBuilder loggingBuilder,\n        TEnricher enricher) where TEnricher : ILogHelperLoggingEnricher\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddEnricher(enricher);\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithEnricher<TEnricher>(this ILogHelperLoggingBuilder loggingBuilder) where TEnricher : ILogHelperLoggingEnricher, new()\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddEnricher(new TEnricher());\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder WithEnricher<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TEnricher>(this ILogHelperLoggingBuilder loggingBuilder, params object[] ctorParams) where TEnricher : ILogHelperLoggingEnricher\n    {\n        loggingBuilder.AddEnricher(ActivatorHelper.CreateInstance<TEnricher>(ctorParams));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder EnrichWithProperty(this ILogHelperLoggingBuilder loggingBuilder, string propertyName, object value, bool overwrite = false)\n    {\n        loggingBuilder.AddEnricher(new PropertyLoggingEnricher(propertyName, value, overwrite));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder EnrichWithProperty(this ILogHelperLoggingBuilder loggingBuilder, string propertyName, Func<LogHelperLoggingEvent> valueFactory, bool overwrite = false)\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddEnricher(new PropertyLoggingEnricher(propertyName, valueFactory, overwrite));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder EnrichWithProperty(this ILogHelperLoggingBuilder loggingBuilder, string propertyName, object value, Func<LogHelperLoggingEvent, bool> predict, bool overwrite = false)\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddEnricher(new PropertyLoggingEnricher(propertyName, _ => value, predict, overwrite));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder EnrichWithProperty(this ILogHelperLoggingBuilder loggingBuilder, string propertyName, Func<LogHelperLoggingEvent, object> valueFactory, Func<LogHelperLoggingEvent, bool> predict, bool overwrite = false)\n    {\n        Guard.NotNull(loggingBuilder, nameof(loggingBuilder));\n\n        loggingBuilder.AddEnricher(new PropertyLoggingEnricher(propertyName, valueFactory, predict, overwrite));\n        return loggingBuilder;\n    }\n\n    #endregion ILogHelperLoggingBuilder\n\n    #region LoggingEnricher\n\n    public static void AddProperty(this LogHelperLoggingEvent loggingEvent, string propertyName,\n        object propertyValue, bool overwrite = false)\n    {\n        Guard.NotNull(loggingEvent, nameof(loggingEvent));\n\n        loggingEvent.Properties ??= [];\n        if (loggingEvent.Properties.ContainsKey(propertyName) && !overwrite)\n        {\n            return;\n        }\n\n        loggingEvent.Properties[propertyName] = propertyValue;\n    }\n\n    public static void AddProperty(this LogHelperLoggingEvent loggingEvent, string propertyName,\n        Func<LogHelperLoggingEvent, object> propertyValueFactory, bool overwrite = false)\n    {\n        Guard.NotNull(loggingEvent, nameof(loggingEvent));\n        Guard.NotNull(propertyValueFactory, nameof(propertyValueFactory));\n\n        loggingEvent.Properties ??= [];\n\n        if (loggingEvent.Properties.ContainsKey(propertyName) && !overwrite)\n        {\n            return;\n        }\n\n        loggingEvent.Properties[propertyName] = propertyValueFactory.Invoke(loggingEvent);\n    }\n\n    #endregion LoggingEnricher\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LogHelperFactory.cs",
    "content": "﻿using System.Collections.Concurrent;\n\nnamespace WeihanLi.Common.Logging;\n\npublic interface ILogHelperFactory : IDisposable\n{\n    /// <summary>\n    /// Creates a new ILogHelper instance.\n    /// </summary>\n    /// <param name=\"categoryName\">The category name for messages produced by the logger.</param>\n    ILogHelperLogger CreateLogger(string categoryName);\n}\n\ninternal sealed class NullLogHelperFactory : ILogHelperFactory\n{\n    public static readonly ILogHelperFactory Instance = new NullLogHelperFactory();\n\n    public void Dispose()\n    {\n        // nothing to dispose\n    }\n\n    private NullLogHelperFactory()\n    {\n    }\n\n    public ILogHelperLogger CreateLogger(string categoryName) => NullLogHelperLogger.Instance;\n}\n\ninternal sealed class LogHelperFactory(IReadOnlyDictionary<Type, ILogHelperProvider> logHelperProviders,\n    IReadOnlyCollection<ILogHelperLoggingEnricher> logHelperEnrichers,\n    IReadOnlyCollection<Func<Type, LogHelperLoggingEvent, bool>> logFilters\n        ) : ILogHelperFactory\n{\n    internal readonly IReadOnlyDictionary<Type, ILogHelperProvider> _logHelperProviders = logHelperProviders;\n    internal readonly IReadOnlyCollection<ILogHelperLoggingEnricher> _logHelperEnrichers = logHelperEnrichers;\n    internal readonly IReadOnlyCollection<Func<Type, LogHelperLoggingEvent, bool>> _logFilters = logFilters;\n\n    private readonly ConcurrentDictionary<string, ILogHelperLogger> _loggers = new();\n\n    public ILogHelperLogger CreateLogger(string categoryName) => _loggers.GetOrAdd(categoryName, _ => new LogHelper(this, _));\n\n    public void Dispose()\n    {\n        if (_logHelperProviders.Count == 0)\n            return;\n\n        foreach (var provider in _logHelperProviders.Values)\n        {\n            if (provider is IDisposable disposable)\n            {\n                disposable.Dispose();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LogHelperLogLevel.cs",
    "content": "﻿namespace WeihanLi.Common.Logging;\n\n/// <summary>\n/// LogLevel\n/// </summary>\npublic enum LogHelperLogLevel\n{\n    /// <summary>\n    /// All logging levels\n    /// </summary>\n    All = 0,\n\n    /// <summary>\n    /// A trace logging level\n    /// </summary>\n    Trace = 1,\n\n    /// <summary>\n    /// A debug logging level\n    /// </summary>\n    Debug = 2,\n\n    /// <summary>\n    /// A info logging level\n    /// </summary>\n    Info = 4,\n\n    /// <summary>\n    /// A warn logging level\n    /// </summary>\n    Warn = 8,\n\n    /// <summary>\n    /// An error logging level\n    /// </summary>\n    Error = 16,\n\n    /// <summary>\n    /// A fatal logging level\n    /// </summary>\n    Fatal = 32,\n\n    /// <summary>\n    /// None\n    /// </summary>\n    None = 64\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LogHelperLogger.cs",
    "content": "﻿namespace WeihanLi.Common.Logging;\n\npublic interface ILogHelperLogger\n{\n    void Log(LogHelperLogLevel logLevel, Exception? exception, string? messageTemplate, params object?[] parameters);\n\n    bool IsEnabled(LogHelperLogLevel logLevel);\n}\n\ninternal sealed class NullLogHelperLogger : ILogHelperLogger\n{\n    public static readonly ILogHelperLogger Instance = new NullLogHelperLogger();\n\n    private NullLogHelperLogger()\n    {\n    }\n\n    public void Log(LogHelperLogLevel logLevel, Exception? exception, string? messageTemplate, params object?[] parameters)\n    {\n        // empty\n    }\n\n    public bool IsEnabled(LogHelperLogLevel logLevel) => false;\n}\n\n// ReSharper disable once UnusedTypeParameter\npublic interface ILogHelperLogger<TCategory> : ILogHelperLogger;\n\ninternal sealed class LogHelperGenericLogger<TCategory>(LogHelperFactory logHelperFactory) : LogHelper(logHelperFactory, typeof(TCategory).FullName ?? typeof(TCategory).Name), ILogHelperLogger<TCategory>;\n\ninternal class LogHelper(LogHelperFactory logHelperFactory, string categoryName) : ILogHelperLogger\n{\n    private readonly LogHelperFactory _logHelperFactory = logHelperFactory;\n\n    public string CategoryName { get; } = categoryName;\n\n    public void Log(LogHelperLogLevel logLevel, Exception? exception, string? messageTemplate, params object?[] parameters)\n    {\n        if (!IsEnabled(logLevel))\n            return;\n\n        var loggingEvent = new LogHelperLoggingEvent()\n        {\n            CategoryName = CategoryName,\n            DateTime = DateTimeOffset.UtcNow,\n            Exception = exception,\n            LogLevel = logLevel,\n            MessageTemplate = messageTemplate ?? string.Empty,\n        };\n\n        if (_logHelperFactory._logFilters.Count > 0 &&\n            !_logHelperFactory._logFilters.Any(x => x.Invoke(typeof(int), loggingEvent))\n            )\n        {\n            return;\n        }\n\n        var formattedLog = LoggingFormatter.Format(loggingEvent.MessageTemplate, parameters);\n        loggingEvent.Message = formattedLog.Msg;\n        loggingEvent.Properties = formattedLog.Values;\n\n        foreach (var enricher in _logHelperFactory._logHelperEnrichers)\n        {\n            enricher.Enrich(loggingEvent);\n        }\n\n        Parallel.ForEach(_logHelperFactory._logHelperProviders, logHelperProvider =>\n        {\n            if (_logHelperFactory._logFilters.Count == 0\n                || _logHelperFactory._logFilters.All(x => x.Invoke(logHelperProvider.Key, loggingEvent)))\n            {\n                logHelperProvider.Value.Log(loggingEvent);\n            }\n        });\n    }\n\n    public bool IsEnabled(LogHelperLogLevel logLevel) => logLevel != LogHelperLogLevel.None;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LogHelperLoggingEvent.cs",
    "content": "﻿namespace WeihanLi.Common.Logging;\n\npublic class LogHelperLoggingEvent\n{\n    public string CategoryName { get; set; } = null!;\n\n    public DateTimeOffset DateTime { get; set; }\n\n    public string MessageTemplate { get; set; } = null!;\n\n    public string Message { get; set; } = null!;\n\n    public Exception? Exception { get; set; }\n\n    public LogHelperLogLevel LogLevel { get; set; }\n\n    public Dictionary<string, object?>? Properties { get; set; }\n\n    public LogHelperLoggingEvent Copy()\n    {\n        var newEvent = (LogHelperLoggingEvent)MemberwiseClone();\n        if (Properties != null)\n        {\n            newEvent.Properties = [];\n            foreach (var property in Properties)\n            {\n                newEvent.Properties[property.Key] = property.Value;\n            }\n        }\n        return newEvent;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LogHelperProvider.cs",
    "content": "﻿namespace WeihanLi.Common.Logging;\n\npublic interface ILogHelperProvider\n{\n    void Log(LogHelperLoggingEvent loggingEvent);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LoggerGeneric.cs",
    "content": "﻿using Microsoft.Extensions.Logging;\nusing Microsoft.Extensions.Options;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Logging;\n\npublic sealed class GenericLoggerOptions\n{\n    public Func<Type, bool>? FullNamePredict { get; set; } = _ => true;\n}\n\ninternal sealed class GenericLogger<T> : ILogger<T>\n{\n    private readonly ILogger _logger;\n\n    /// <summary>\n    /// Creates a new <see cref=\"GenericLogger{T}\"/>.\n    /// </summary>\n    /// <param name=\"factory\">The factory.</param>\n    /// <param name=\"options\">GenericLoggerOptions</param>\n    public GenericLogger(ILoggerFactory factory, IOptions<GenericLoggerOptions> options)\n    {\n        Guard.NotNull(factory);\n        var includeGenericParameters = options.Value.FullNamePredict?.Invoke(typeof(T)) == true;\n        _logger = factory.CreateLogger(TypeHelper.GetTypeDisplayName(typeof(T), includeGenericParameters: includeGenericParameters, nestedTypeDelimiter: '.'));\n    }\n\n    /// <inheritdoc />\n    IDisposable? ILogger.BeginScope<TState>(TState state)\n    {\n        return _logger.BeginScope(state);\n    }\n\n    /// <inheritdoc />\n    bool ILogger.IsEnabled(LogLevel logLevel)\n    {\n        return _logger.IsEnabled(logLevel);\n    }\n\n    /// <inheritdoc />\n    void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)\n    {\n        _logger.Log(logLevel, eventId, state, exception, formatter);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LoggingBuilder.cs",
    "content": "﻿using WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Logging;\n\npublic interface ILogHelperLoggingBuilder\n{\n    /// <summary>\n    /// Adds an ILogHelperProvider to the logging system.\n    /// </summary>\n    /// <param name=\"provider\">The ILogHelperProvider.</param>\n    bool AddProvider(ILogHelperProvider provider);\n\n    /// <summary>\n    /// add log enricher\n    /// </summary>\n    /// <param name=\"enricher\">log enricher</param>\n    /// <returns></returns>\n    bool AddEnricher(ILogHelperLoggingEnricher enricher);\n\n    /// <summary>\n    /// Add logs filter\n    /// </summary>\n    /// <param name=\"filterFunc\">filterFunc, logProviderType/categoryName/Exception, whether to write log</param>\n    bool AddFilter(Func<Type, LogHelperLoggingEvent, bool> filterFunc);\n\n    /// <summary>\n    /// Build for LogFactory\n    /// </summary>\n    /// <returns></returns>\n    ILogHelperFactory Build();\n}\n\ninternal class LogHelperLoggingBuilder : ILogHelperLoggingBuilder\n{\n    internal readonly Dictionary<Type, ILogHelperProvider> _logHelperProviders = [];\n    internal readonly List<ILogHelperLoggingEnricher> _logHelperEnrichers = [];\n    internal readonly List<Func<Type, LogHelperLoggingEvent, bool>> _logFilters = [];\n\n    public bool AddProvider(ILogHelperProvider provider)\n    {\n        Guard.NotNull(provider, nameof(provider));\n\n        return _logHelperProviders.AddIfNotContainsKey(provider.GetType(), provider);\n    }\n\n    public bool AddEnricher(ILogHelperLoggingEnricher enricher)\n    {\n        Guard.NotNull(enricher, nameof(enricher));\n        _logHelperEnrichers.Add(enricher);\n        return true;\n    }\n\n    public bool AddFilter(Func<Type, LogHelperLoggingEvent, bool> filterFunc)\n    {\n        Guard.NotNull(filterFunc, nameof(filterFunc));\n\n        _logFilters.Add(filterFunc);\n        return true;\n    }\n\n    public ILogHelperFactory Build() => new LogHelperFactory(_logHelperProviders, _logHelperEnrichers, _logFilters);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LoggingEnricher.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Logging;\n\npublic interface ILogHelperLoggingEnricher : IEnricher<LogHelperLoggingEvent>;\n\ninternal sealed class PropertyLoggingEnricher : PropertyEnricher<LogHelperLoggingEvent>, ILogHelperLoggingEnricher\n{\n    public PropertyLoggingEnricher(string propertyName, object propertyValue, bool overwrite = false) : base(propertyName, propertyValue, overwrite)\n    {\n    }\n\n    public PropertyLoggingEnricher(string propertyName, Func<LogHelperLoggingEvent, object> propertyValueFactory, bool overwrite = false) : base(propertyName, propertyValueFactory, overwrite)\n    {\n    }\n\n    public PropertyLoggingEnricher(string propertyName, Func<LogHelperLoggingEvent, object> propertyValueFactory, Func<LogHelperLoggingEvent, bool> propertyPredict, bool overwrite = false) : base(propertyName, propertyValueFactory, propertyPredict, overwrite)\n    {\n    }\n\n    protected override Action<LogHelperLoggingEvent, string, Func<LogHelperLoggingEvent, object>, bool> EnrichAction =>\n        (invocation, propertyName, propertyValueFactory, overwrite) =>\n            invocation.AddProperty(propertyName, propertyValueFactory, overwrite);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/LoggingFormatter.cs",
    "content": "﻿using System.Collections;\nusing System.Globalization;\nusing System.Text;\n\nnamespace WeihanLi.Common.Logging;\n\ninternal struct FormattedLogValue(string msg, Dictionary<string, object?>? values)\n{\n    public string Msg { get; set; } = msg;\n\n    public Dictionary<string, object?>? Values { get; set; } = values;\n}\n\ninternal static class LoggingFormatter\n{\n    public static FormattedLogValue Format(string msgTemplate, object?[]? values)\n    {\n        if (values == null || values.Length == 0)\n            return new FormattedLogValue(msgTemplate, null);\n\n        var formatter = new LogValuesFormatter(msgTemplate);\n        var msg = formatter.FormatWithValues(values);\n        var dic = formatter.GetValues(values)\n            .ToDictionary(x => x.Key, x => x.Value);\n\n        return new FormattedLogValue(msg, dic!);\n    }\n\n    /// <summary>\n    /// Formatter to convert the named format items like {NamedFormatItem} to <see cref=\"M:string.Format\"/> format.\n    /// </summary>\n    private class LogValuesFormatter\n    {\n        private const string NullValue = \"(null)\";\n        private static readonly char[] _formatDelimiters = [':'];\n        private readonly string _format;\n        private readonly List<string> _valueNames = [];\n\n        public LogValuesFormatter(string format)\n        {\n            OriginalFormat = format;\n\n            var sb = new StringBuilder();\n            var scanIndex = 0;\n            var endIndex = format.Length;\n\n            while (scanIndex < endIndex)\n            {\n                var openBraceIndex = FindBraceIndex(format, '{', scanIndex, endIndex);\n                var closeBraceIndex = FindBraceIndex(format, '}', openBraceIndex, endIndex);\n\n                if (closeBraceIndex == endIndex)\n                {\n                    sb.Append(format, scanIndex, endIndex - scanIndex);\n                    scanIndex = endIndex;\n                }\n                else\n                {\n                    // Format item syntax : { index[,alignment][ :formatString] }.\n                    var formatDelimiterIndex = FindIndexOfAny(format, _formatDelimiters, openBraceIndex, closeBraceIndex);\n\n                    sb.Append(format, scanIndex, openBraceIndex - scanIndex + 1);\n                    sb.Append(_valueNames.Count.ToString(CultureInfo.InvariantCulture));\n                    _valueNames.Add(format.Substring(openBraceIndex + 1, formatDelimiterIndex - openBraceIndex - 1));\n                    sb.Append(format, formatDelimiterIndex, closeBraceIndex - formatDelimiterIndex + 1);\n\n                    scanIndex = closeBraceIndex + 1;\n                }\n            }\n\n            _format = sb.ToString();\n        }\n\n        private string OriginalFormat { get; }\n\n        private static int FindBraceIndex(string format, char brace, int startIndex, int endIndex)\n        {\n            // Example: {{prefix{{{Argument}}}suffix}}.\n            var braceIndex = endIndex;\n            var scanIndex = startIndex;\n            var braceOccurrenceCount = 0;\n\n            while (scanIndex < endIndex)\n            {\n                if (braceOccurrenceCount > 0 && format[scanIndex] != brace)\n                {\n                    if (braceOccurrenceCount % 2 == 0)\n                    {\n                        // Even number of '{' or '}' found. Proceed search with next occurrence of '{' or '}'.\n                        braceOccurrenceCount = 0;\n                        braceIndex = endIndex;\n                    }\n                    else\n                    {\n                        // An unescaped '{' or '}' found.\n                        break;\n                    }\n                }\n                else if (format[scanIndex] == brace)\n                {\n                    if (brace == '}')\n                    {\n                        if (braceOccurrenceCount == 0)\n                        {\n                            // For '}' pick the first occurrence.\n                            braceIndex = scanIndex;\n                        }\n                    }\n                    else\n                    {\n                        // For '{' pick the last occurrence.\n                        braceIndex = scanIndex;\n                    }\n\n                    braceOccurrenceCount++;\n                }\n\n                scanIndex++;\n            }\n\n            return braceIndex;\n        }\n\n        private static int FindIndexOfAny(string format, char[] chars, int startIndex, int endIndex)\n        {\n            var findIndex = format.IndexOfAny(chars, startIndex, endIndex - startIndex);\n            return findIndex == -1 ? endIndex : findIndex;\n        }\n\n        public string FormatWithValues(object?[]? values)\n        {\n            if (values != null)\n            {\n                for (var i = 0; i < values.Length; i++)\n                {\n                    values[i] = FormatArgument(values[i]);\n                }\n            }\n\n            return string.Format(CultureInfo.InvariantCulture, _format, values ?? []);\n        }\n\n        public IEnumerable<KeyValuePair<string, object?>> GetValues(object?[] values)\n        {\n            var valueArray = new KeyValuePair<string, object?>[values.Length + 1];\n            for (var index = 0; index != _valueNames.Count; ++index)\n            {\n                valueArray[index] = new KeyValuePair<string, object?>(_valueNames[index], values[index]);\n            }\n\n            valueArray[^1] = new KeyValuePair<string, object?>(\"{OriginalFormat}\", OriginalFormat);\n            return valueArray;\n        }\n\n        private static object FormatArgument(object? value)\n        {\n            if (value == null)\n            {\n                return NullValue;\n            }\n\n            // since 'string' implements IEnumerable, special case it\n            if (value is string)\n            {\n                return value;\n            }\n\n            // if the value implements IEnumerable, build a comma separated string.\n            if (value is IEnumerable enumerable)\n            {\n                return string.Join(\", \", enumerable.Cast<object>().Select(o => o ?? NullValue));\n            }\n\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/MicrosoftLoggingLogHelperProvider.cs",
    "content": "﻿using Microsoft.Extensions.Logging;\n\nnamespace WeihanLi.Common.Logging;\n\ninternal class MicrosoftLoggingLogHelperProvider(ILoggerFactory loggerFactory) : ILogHelperProvider\n{\n    private readonly ILoggerFactory _loggerFactory = loggerFactory;\n\n    public void Log(LogHelperLoggingEvent loggingEvent)\n    {\n        var logger = _loggerFactory.CreateLogger(loggingEvent.CategoryName);\n        _ = LogInternal(logger, loggingEvent);\n    }\n\n    private static bool LogInternal(ILogger logger, LogHelperLoggingEvent loggingEvent)\n    {\n        var logLevel = ConvertLogLevel(loggingEvent.LogLevel);\n        if (!logger.IsEnabled(logLevel))\n        {\n            return false;\n        }\n        var logged = false;\n        switch (loggingEvent.LogLevel)\n        {\n            case LogHelperLogLevel.Debug:\n                logger.LogDebug(loggingEvent.Exception, loggingEvent.Message);\n                logged = true;\n                break;\n\n            case LogHelperLogLevel.Trace:\n                logger.LogTrace(loggingEvent.Exception, loggingEvent.Message);\n                logged = true;\n                break;\n\n            case LogHelperLogLevel.Info:\n                logger.LogInformation(loggingEvent.Exception, loggingEvent.Message);\n                logged = true;\n                break;\n\n            case LogHelperLogLevel.Warn:\n                logger.LogWarning(loggingEvent.Exception, loggingEvent.Message);\n                logged = true;\n                break;\n\n            case LogHelperLogLevel.Error:\n                logger.LogError(loggingEvent.Exception, loggingEvent.Message);\n                logged = true;\n                break;\n\n            case LogHelperLogLevel.Fatal:\n                logger.LogCritical(loggingEvent.Exception, loggingEvent.Message);\n                logged = true;\n                break;\n        }\n\n        return logged;\n    }\n\n    private static LogLevel ConvertLogLevel(LogHelperLogLevel logHelperLevel)\n    {\n        return logHelperLevel switch\n        {\n            LogHelperLogLevel.All => LogLevel.Debug,\n            LogHelperLogLevel.Info => LogLevel.Information,\n            LogHelperLogLevel.Debug => LogLevel.Debug,\n            LogHelperLogLevel.Trace => LogLevel.Trace,\n            LogHelperLogLevel.Warn => LogLevel.Warning,\n            LogHelperLogLevel.Error => LogLevel.Error,\n            LogHelperLogLevel.Fatal => LogLevel.Critical,\n            LogHelperLogLevel.None => LogLevel.None,\n            _ => LogLevel.Warning,\n        };\n    }\n}\n\ninternal static class MicrosoftLoggingExtensions\n{\n    internal static void AddMicrosoftLogging(this ILogHelperLoggingBuilder logHelperFactory, ILoggerFactory loggerFactory)\n    {\n        logHelperFactory.AddProvider(new MicrosoftLoggingLogHelperProvider(loggerFactory));\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/MicrosoftLoggingLoggerExtensions.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.DependencyInjection.Extensions;\nusing Newtonsoft.Json;\nusing System.Collections.Concurrent;\nusing System.Diagnostics;\nusing WeihanLi.Common;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Logging;\nusing WeihanLi.Common.Services;\nusing WeihanLi.Extensions;\n\n// ReSharper disable once CheckNamespace\nnamespace Microsoft.Extensions.Logging;\n\n[ProviderAlias(\"Delegate\")]\ninternal sealed class DelegateLoggerProvider(Action<string, LogLevel, Exception?, string> logAction) : ILoggerProvider\n{\n    internal static ILoggerProvider Default { get; } = new DelegateLoggerProvider((category, level, exception, msg) =>\n    {\n        var (foregroundColor, backgroundColor) = GetConsoleColorForLogLevel(level);\n        var levelText = GetLogLevelText(level);\n        var dateTime = DateTimeOffset.Now;\n        var message = @$\"[{levelText}][{category}] {dateTime} {msg}\";\n        if (exception is not null)\n        {\n            message = $\"{message}{Environment.NewLine}{exception}\";\n        }\n\n        ConsoleHelper.WriteLineWithColor(message, foregroundColor, backgroundColor);\n        if (level is LogLevel.Trace)\n        {\n            Trace.WriteLine(message);\n        }\n\n        return;\n\n        static (ConsoleColor? ForegroundColor, ConsoleColor? BackgroundColor) GetConsoleColorForLogLevel(LogLevel logLevel)\n            => logLevel switch\n            {\n                LogLevel.Trace or LogLevel.Debug => (ConsoleColor.DarkGray, ConsoleColor.Black),\n                LogLevel.Information => (ConsoleColor.DarkGreen, ConsoleColor.Black),\n                LogLevel.Warning => (ConsoleColor.Yellow, ConsoleColor.Black),\n                LogLevel.Error => (ConsoleColor.Black, ConsoleColor.DarkRed),\n                LogLevel.Critical => (ConsoleColor.White, ConsoleColor.DarkRed),\n                _ => (null, null)\n            };\n\n        static string GetLogLevelText(LogLevel logLevel)\n            => logLevel switch\n            {\n                LogLevel.Trace => \"trce\",\n                LogLevel.Debug => \"dbug\",\n                LogLevel.Information => \"info\",\n                LogLevel.Warning => \"warn\",\n                LogLevel.Error => \"fail\",\n                LogLevel.Critical => \"crit\",\n                _ => logLevel.ToString().ToLowerInvariant()\n            };\n    });\n\n    private readonly ConcurrentDictionary<string, DelegateLogger> _loggers = new();\n\n    public void Dispose()\n    {\n        _loggers.Clear();\n    }\n\n    public ILogger CreateLogger(string categoryName)\n    {\n        return _loggers.GetOrAdd(categoryName, category => new DelegateLogger(category, logAction));\n    }\n\n    private sealed class DelegateLogger(string categoryName, Action<string, LogLevel, Exception?, string> logAction) : ILogger\n    {\n        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)\n        {\n            var msg = formatter(state, exception);\n            logAction.Invoke(categoryName, logLevel, exception, msg);\n        }\n\n        public bool IsEnabled(LogLevel logLevel) => true;\n\n\n        IDisposable ILogger.BeginScope<TState>(TState state) => NullScope.Instance;\n    }\n}\n\npublic sealed class FileLoggingOptions\n{\n    public string LogsDirectory { get; set; } = \"Logs\";\n    public string FileFormat { get; set; } = \"app-logs-{date}.log\";\n    // public int FileSizeLimitBytes { get; set; } = 256 * 1024 * 1024;\n    // public int? FilesCountLimit { get; set; } = 100;\n    public LogLevel MinimumLevel { get; set; } = LogLevel.Information;\n    public Func<string, LogLevel, Exception?, string, DateTimeOffset, string?>? LogFormatter { get; set; }\n}\n\n[ProviderAlias(\"File\")]\ninternal sealed class FileLoggerProvider : ILoggerProvider\n{\n    private readonly FileLoggingOptions _options;\n    private readonly FileLoggingProcessor _loggingProcessor;\n    private readonly ConcurrentDictionary<string, FileLogger> _loggers = new();\n    public FileLoggerProvider(FileLoggingOptions options)\n    {\n        _options = options;\n        _options.LogFormatter ??= (category, level, exception, msg, timestamp) => JsonConvert.SerializeObject(new\n        {\n            level,\n            timestamp = timestamp.ToString(\"yyyy-MM-dd HH:mm:ss.fff\"),\n            category,\n            msg,\n            exception = exception?.ToString()\n        }, JsonSerializeExtension.DefaultSerializerSettings);\n        _loggingProcessor = new FileLoggingProcessor(options);\n    }\n\n    public void Dispose() => _loggingProcessor.Dispose();\n\n    public ILogger CreateLogger(string categoryName)\n    {\n        return _loggers.GetOrAdd(categoryName, category => new FileLogger(category, _options, _loggingProcessor));\n    }\n}\n\ninternal sealed class FileLogger(string categoryName, FileLoggingOptions options, FileLoggingProcessor processor) : ILogger\n{\n    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)\n    {\n        if (logLevel < options.MinimumLevel)\n            return;\n\n        var timestamp = DateTimeOffset.Now;\n        var msg = formatter(state, exception);\n        var log = options.LogFormatter!.Invoke(categoryName, logLevel, exception, msg, timestamp);\n        if (log is not null)\n        {\n            processor.EnqueueLog(log, timestamp);\n        }\n    }\n\n    public bool IsEnabled(LogLevel logLevel) => logLevel >= options.MinimumLevel;\n\n    IDisposable ILogger.BeginScope<TState>(TState state) => NullScope.Instance;\n}\n\npublic static class LoggerExtensions\n{\n    #region Info\n\n    public static void Info(this ILogger logger, string msg, params object[] parameters) => logger.LogInformation(msg, parameters);\n\n    public static void Info(this ILogger logger, Exception ex, string msg) => logger.LogInformation(ex, msg);\n\n    #endregion Info\n\n    #region Trace\n\n    public static void Trace(this ILogger logger, string msg, params object[] parameters) => logger.LogTrace(msg, parameters);\n\n    public static void Trace(this ILogger logger, Exception ex, string msg) => logger.LogTrace(ex, msg);\n\n    public static void Trace(this ILogger logger, Exception ex) => logger.LogTrace(ex, ex.Message);\n\n    #endregion Trace\n\n    #region Debug\n\n    public static void Debug(this ILogger logger, string msg, params object[] parameters) => logger.LogDebug(msg, parameters);\n\n    public static void Debug(this ILogger logger, Exception ex, string msg) => logger.LogDebug(ex, msg);\n\n    public static void Debug(this ILogger logger, Exception ex) => logger.LogDebug(ex, ex.Message);\n\n    #endregion Debug\n\n    #region Warn\n\n    public static void Warn(this ILogger logger, string msg, params object[] parameters) => logger.LogWarning(msg, parameters);\n\n    public static void Warn(this ILogger logger, Exception ex, string msg) => logger.LogWarning(ex, msg);\n\n    public static void Warn(this ILogger logger, Exception ex) => logger.LogWarning(ex, ex.Message);\n\n    #endregion Warn\n\n    #region Error\n\n    public static void Error(this ILogger logger, string msg, params object[] parameters) => logger.LogError(msg, parameters);\n\n    public static void Error(this ILogger logger, Exception ex, string msg) => logger.LogError(ex, msg);\n\n    public static void Error(this ILogger logger, Exception ex) => logger.LogError(ex, ex.Message);\n\n    #endregion Error\n\n    #region Fatal\n\n    public static void Fatal(this ILogger logger, string msg, params object[] parameters) => logger.LogCritical(msg, parameters);\n\n    public static void Fatal(this ILogger logger, Exception ex, string msg) => logger.LogCritical(ex, msg);\n\n    public static void Fatal(this ILogger logger, Exception ex) => logger.LogCritical(ex, ex.Message);\n\n    #endregion Fatal\n\n    #region LoggerFactory\n\n    /// <summary>\n    /// AddDelegateLoggerProvider\n    /// </summary>\n    /// <param name=\"loggerFactory\">loggerFactory</param>\n    /// <param name=\"logAction\">logAction</param>\n    /// <returns>loggerFactory</returns>\n    public static ILoggerFactory AddDelegateLogger(this ILoggerFactory loggerFactory, Action<string, LogLevel, Exception?, string> logAction)\n    {\n        loggerFactory.AddProvider(new DelegateLoggerProvider(logAction));\n        return loggerFactory;\n    }\n\n    #endregion LoggerFactory\n\n    #region ILoggingBuilder\n\n    public static ILoggingBuilder AddDefaultDelegateLogger(this ILoggingBuilder loggingBuilder)\n    {\n        return loggingBuilder.AddProvider(DelegateLoggerProvider.Default);\n    }\n\n    public static ILoggingBuilder AddDelegateLogger(this ILoggingBuilder loggingBuilder,\n        Action<string, LogLevel, Exception?, string> logAction)\n    {\n        return loggingBuilder.AddProvider(new DelegateLoggerProvider(logAction));\n    }\n\n    public static ILoggingBuilder AddFile(this ILoggingBuilder loggingBuilder, Action<FileLoggingOptions>? optionsConfigure = null)\n    {\n        var options = new FileLoggingOptions();\n        optionsConfigure?.Invoke(options);\n        return loggingBuilder.AddProvider(new FileLoggerProvider(options));\n    }\n\n    public static ILoggingBuilder UseCustomGenericLogger(this ILoggingBuilder loggingBuilder, Action<GenericLoggerOptions>? genericLoggerConfig = null)\n    {\n        Guard.NotNull(loggingBuilder);\n        if (genericLoggerConfig is not null)\n        {\n            loggingBuilder.Services.Configure(genericLoggerConfig);\n        }\n        loggingBuilder.Services.Replace(new ServiceDescriptor(typeof(ILogger<>), typeof(GenericLogger<>), ServiceLifetime.Singleton));\n        return loggingBuilder;\n    }\n\n    #endregion ILoggingBuilder\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Logging/PeriodBatchingLoggingService.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Helpers.PeriodBatching;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Logging;\n\ninternal sealed class PeriodBatchingLoggingService : PeriodicBatching<LogHelperLoggingEvent>\n{\n    private readonly LogHelperFactory _logHelperFactory;\n    private readonly SemaphoreSlim _semaphore;\n\n    public PeriodBatchingLoggingService(int batchSizeLimit, TimeSpan period, LogHelperFactory logHelperFactory) : base(batchSizeLimit, period)\n    {\n        _logHelperFactory = logHelperFactory;\n        _semaphore = new SemaphoreSlim(_logHelperFactory._logHelperProviders.Count, Environment.ProcessorCount * 8);\n    }\n\n    protected override async Task EmitBatchAsync(IEnumerable<LogHelperLoggingEvent> events)\n    {\n        if (_logHelperFactory._logHelperProviders.Count > 0)\n        {\n            var tasks = events.Select(loggingEvent => _logHelperFactory._logHelperProviders.Select(\n                async logHelperProvider =>\n                {\n                    if (_logHelperFactory._logFilters.All(x => x.Invoke(logHelperProvider.Key, loggingEvent)))\n                    {\n                        try\n                        {\n                            await _semaphore.WaitAsync();\n                            logHelperProvider.Value.Log(loggingEvent);\n                        }\n                        catch (Exception e)\n                        {\n                            InvokeHelper.OnInvokeException?.Invoke(e);\n                        }\n                        finally\n                        {\n                            _semaphore.Release();\n                        }\n                    }\n                }));\n\n            await tasks.SelectMany(t => t).WhenAll();\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/BaseEntity.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.ComponentModel.DataAnnotations;\n\nnamespace WeihanLi.Common.Models;\n\npublic interface IEntity<TKey>\n{\n    TKey Id { get; set; }\n}\n\npublic class BaseEntity<TKey> : IEntity<TKey>\n{\n    public TKey Id { get; set; } = default!;\n}\n\npublic interface IEntityWithUpdatedAt\n{\n    DateTimeOffset UpdatedAt { get; set; }\n}\n\npublic interface IEntityWithCreatedUpdatedAt : IEntityWithUpdatedAt\n{\n    DateTimeOffset CreatedAt { get; set; }\n}\n\npublic interface IEntityWithUpdatedBy\n{\n    string UpdatedBy { get; set; }\n}\n\npublic interface IEntityWithCreatedUpdatedBy : IEntityWithUpdatedBy\n{\n    string CreatedBy { get; set; }\n}\n\npublic interface IEntityWithUpdatedAtAndBy\n    : IEntityWithUpdatedAt, IEntityWithUpdatedBy;\n\npublic interface IEntityWithCreatedUpdatedAtAndBy\n    : IEntityWithCreatedUpdatedAt, IEntityWithCreatedUpdatedBy, IEntityWithUpdatedAtAndBy;\n\npublic interface IEntityWithReviewState\n{\n    ReviewState State { get; set; }\n}\n\npublic interface IEntityWithRemark\n{\n    [StringLength(2048)]\n    string? Remark { get; set; }\n}\n\npublic interface IEntityWithReviewStateAndRemark : IEntityWithReviewState, IEntityWithRemark;\n\npublic class BaseEntityWithDeleted<TKey> : BaseEntity<TKey>, ISoftDeleteEntityWithDeleted\n{\n    public bool IsDeleted { get; set; }\n}\n\npublic class BaseEntityWithUpdatedAt<TKey> : BaseEntity<TKey>, IEntityWithUpdatedAt\n{\n    public DateTimeOffset UpdatedAt { get; set; }\n}\n\npublic class BaseEntityWithCreatedUpdatedAt<TKey> : BaseEntityWithUpdatedAt<TKey>, IEntityWithCreatedUpdatedAt\n{\n    public DateTimeOffset CreatedAt { get; set; }\n}\n\npublic class BaseEntityWithCreatedUpdatedAtAndDeleted<TKey> : BaseEntityWithCreatedUpdatedAt<TKey>,\n    ISoftDeleteEntityWithDeleted\n{\n    public bool IsDeleted { get; set; }\n}\n\npublic class BaseEntityWithCreatedUpdatedAtAndDeletedAndRemark<TKey>\n    : BaseEntityWithCreatedUpdatedAtAndDeleted<TKey>,\n    IEntityWithRemark\n{\n    [StringLength(2048)]\n    public string? Remark { get; set; }\n}\n\npublic class BaseEntityWithUpdatedAtAndBy<TKey>\n    : BaseEntityWithUpdatedAt<TKey>, IEntityWithUpdatedAtAndBy\n{\n    [StringLength(256)]\n    public string UpdatedBy { get; set; } = default!;\n}\n\npublic class BaseEntityWithCreatedUpdatedAtAndBy<TKey>\n    : BaseEntityWithCreatedUpdatedAt<TKey>, IEntityWithCreatedUpdatedAtAndBy\n{\n    [StringLength(256)]\n    public string CreatedBy { get; set; } = default!;\n    [StringLength(256)]\n    public string UpdatedBy { get; set; } = default!;\n}\n\npublic class BaseEntityWithUpdatedAtAndByAndDeleted<TKey> : BaseEntityWithUpdatedAtAndBy<TKey>,\n    ISoftDeleteEntityWithDeleted\n{\n    public bool IsDeleted { get; set; }\n}\n\npublic class BaseEntityWithCreatedUpdatedAtAndByAndDeleted<TKey> : BaseEntityWithCreatedUpdatedAtAndBy<TKey>,\n    ISoftDeleteEntityWithDeleted\n{\n    public bool IsDeleted { get; set; }\n}\n\npublic class BaseEntityWithReviewState<TKey> : BaseEntity<TKey>, IEntityWithReviewState\n{\n    public ReviewState State { get; set; }\n}\n\npublic class BaseEntityWithReviewStateAndRemark<TKey>\n    : BaseEntityWithReviewState<TKey>, IEntityWithReviewStateAndRemark\n{\n    [StringLength(2048)]\n    public string? Remark { get; set; }\n}\n\npublic class BaseEntityWithReviewStateWithDeleted<TKey>\n    : BaseEntityWithDeleted<TKey>, IEntityWithReviewState\n{\n    public ReviewState State { get; set; }\n}\n\npublic class BaseEntityWithCreatedUpdatedAtAndDeletedAndReviewState<TKey> :\n    BaseEntityWithCreatedUpdatedAtAndDeleted<TKey>, IEntityWithReviewState\n{\n    public ReviewState State { get; set; }\n}\n\npublic class BaseEntityWithCreatedUpdatedAtAndDeletedAndReviewStateAndRemark<TKey> :\n    BaseEntityWithCreatedUpdatedAtAndDeletedAndReviewState<TKey>, IEntityWithReviewStateAndRemark\n{\n    [StringLength(2048)]\n    public string? Remark { get; set; }\n}\n\npublic class BaseEntityWithCreatedUpdatedAtAndByAndDeletedAndReviewState<TKey> :\n    BaseEntityWithCreatedUpdatedAtAndByAndDeleted<TKey>, IEntityWithReviewState\n{\n    public ReviewState State { get; set; }\n}\n\npublic class BaseEntityWithCreatedUpdatedAtAndByAndDeletedAndReviewStateAndRemark<TKey> :\n    BaseEntityWithCreatedUpdatedAtAndByAndDeletedAndReviewState<TKey>, IEntityWithReviewStateAndRemark\n{\n    [StringLength(2048)]\n    public string? Remark { get; set; }\n}\n\npublic class BaseEntity : BaseEntity<int>;\n\npublic class BaseEntityWithDeleted : BaseEntityWithDeleted<int>;\n\npublic class BaseEntityWithReviewState : BaseEntityWithReviewState<int>;\n\npublic class BaseEntityWithReviewStateAndRemark : BaseEntityWithReviewStateAndRemark<int>;\n\npublic class BaseEntityWithReviewStateWithDeleted\n    : BaseEntityWithReviewStateWithDeleted<int>;\n\npublic class BaseEntityWithCreatedUpdatedAt\n    : BaseEntityWithCreatedUpdatedAt<int>;\n\npublic class BaseEntityWithCreatedUpdatedAtAndDeleted\n    : BaseEntityWithCreatedUpdatedAtAndDeleted<int>;\n\npublic class BaseEntityWithCreatedUpdatedAtAndDeletedAndRemark\n    : BaseEntityWithCreatedUpdatedAtAndDeletedAndRemark<int>;\n\npublic class BaseEntityWithCreatedUpdatedAtAndDeletedAndReviewState\n    : BaseEntityWithCreatedUpdatedAtAndDeletedAndReviewState<int>;\n\npublic class BaseEntityWithCreatedUpdatedAtAndDeletedAndReviewStateAndRemark\n    : BaseEntityWithCreatedUpdatedAtAndDeletedAndReviewStateAndRemark<int>;\n\npublic class BaseEntityWithCreatedUpdatedAtAndBy\n    : BaseEntityWithCreatedUpdatedAtAndBy<int>;\n\npublic class BaseEntityWithCreatedUpdatedAtAndByAndDeleted\n    : BaseEntityWithCreatedUpdatedAtAndByAndDeleted<int>;\n\npublic class BaseEntityWithCreatedUpdatedAtAndByAndDeletedAndReviewState\n    : BaseEntityWithCreatedUpdatedAtAndByAndDeletedAndReviewState<int>;\n\npublic class BaseEntityWithCreatedUpdatedAtAndByAndDeletedAndReviewStateAndRemark\n    : BaseEntityWithCreatedUpdatedAtAndByAndDeletedAndReviewStateAndRemark<int>;\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/Category.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.ComponentModel.DataAnnotations;\n\nnamespace WeihanLi.Common.Models;\n\npublic record Category<TKey>\n{\n    public TKey Id { get; set; } = default!;\n\n    [StringLength(256)]\n    [Required]\n    public string Name { get; set; } = null!;\n\n    public TKey ParentId { get; set; } = default!;\n}\n\npublic record Category : Category<int>;\n\npublic record CategoryWithDesc : Category<int>\n{\n    [StringLength(2048)]\n    public string? Description { get; set; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/DataOperationType.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\n/// <summary>\n/// DataOperationType\n/// </summary>\npublic enum DataOperationType\n{\n    Query = 0,\n    Add = 1,\n    Delete = 2,\n    Update = 3,\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/IdNameModel.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\npublic record IdNameModel<TKey>\n{\n    public TKey Id { get; set; } = default!;\n    public string Name { get; set; } = null!;\n\n    public void Deconstruct(out TKey id, out string name)\n    {\n        id = Id;\n        name = Name;\n    }\n}\n\npublic record IdNameDescModel<TKey> : IdNameModel<TKey>\n{\n    public string? Description { get; set; }\n\n    public void Deconstruct(out TKey id, out string name, out string? description)\n    {\n        id = Id;\n        name = Name;\n        description = Description;\n    }\n}\n\npublic record IdNameModel : IdNameModel<int>;\n\npublic record IdNameDescModel : IdNameDescModel<int>;\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/KeyEntry.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\npublic class KeyEntry\n{\n    public string PropertyName { get; set; } = null!;\n\n    public string ColumnName { get; set; } = null!;\n\n    public object? Value { get; set; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/ModelValidator.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.ComponentModel.DataAnnotations;\nusing System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Models;\n\npublic static class ModelValidator\n{\n    [RequiresUnreferencedCode(\"The Type of instance cannot be statically discovered.\")]\n    public static bool TryValidate(object instance, out string? result)\n    {\n        if (TryValidate(instance, out IReadOnlyDictionary<string, string>? results))\n        {\n            result = null;\n            return true;\n        }\n\n        result = results?.ToJson();\n        return false;\n    }\n\n    [RequiresUnreferencedCode(\"The Type of instance cannot be statically discovered.\")]\n    public static bool TryValidate(object instance, out IReadOnlyDictionary<string, string>? result)\n    {\n        if (TryValidate(instance, out IReadOnlyCollection<System.ComponentModel.DataAnnotations.ValidationResult>? results))\n        {\n            result = null;\n            return true;\n        }\n\n        result = results?.SelectMany(r => r.MemberNames.Select(m => new KeyValuePair<string, string>(m, r.ErrorMessage!)))\n            .GroupBy(kv => kv.Key, kv => kv.Value)\n            .ToDictionary(kv => kv.Key, kv => kv.First());\n        return false;\n    }\n\n    [RequiresUnreferencedCode(\"The Type of instance cannot be statically discovered.\")]\n    private static bool TryValidate(object instance, out IReadOnlyCollection<System.ComponentModel.DataAnnotations.ValidationResult>? result)\n    {\n        var results = new List<System.ComponentModel.DataAnnotations.ValidationResult>();\n        if (Validator.TryValidateObject(instance, new ValidationContext(instance, null, null), results, true))\n        {\n            result = null;\n            return true;\n        }\n\n        result = results.AsReadOnly();\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/Ordering.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\npublic enum Ordering\n{\n    Ascending = 0,\n    Descending = 1\n}\n\n[Flags]\npublic enum RangeInclusion\n{\n    None = 0,\n    IncludeLowerBound = 1,\n    IncludeUpperBound = 2\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/PagedListResult.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\npublic interface IPagedListResult<out T> : IListResultWithTotal<T>\n{\n    int Count { get; }\n\n    /// <summary>\n    /// PageNumber\n    /// </summary>\n    int PageNumber { get; }\n\n    /// <summary>\n    /// PageSize\n    /// </summary>\n    int PageSize { get; }\n\n    /// <summary>\n    /// PageCount\n    /// </summary>\n    int PageCount { get; }\n}\n\npublic interface IListResultWithTotal<out T>\n{\n    IReadOnlyList<T> Data { get; }\n\n    int TotalCount { get; }\n}\n\npublic static class EnumerableExtensions\n{\n    public static IEnumerator<T> GetEnumerator<T>(this IListResultWithTotal<T> listResult)\n        => listResult.Data.GetEnumerator();\n}\n\npublic class ListResultWithTotal<T> : IListResultWithTotal<T>\n{\n    public static readonly ListResultWithTotal<T> Empty = new();\n\n    private IReadOnlyList<T> _data = Array.Empty<T>();\n\n    public IReadOnlyList<T> Data\n    {\n        get => _data;\n        set => _data = Guard.NotNull(value, nameof(value));\n    }\n\n    public int TotalCount { get; set; }\n}\n\n/// <summary>\n/// 分页Model\n/// </summary>\n/// <typeparam name=\"T\">Type</typeparam>\n[Serializable]\npublic class PagedListResult<T> : IPagedListResult<T>\n{\n    public static readonly PagedListResult<T> Empty = new();\n\n    private IReadOnlyList<T> _data = Array.Empty<T>();\n\n    public IReadOnlyList<T> Data\n    {\n        get => _data;\n        set => _data = Guard.NotNull(value, nameof(value));\n    }\n\n    private int _pageNumber = 1;\n\n    public int PageNumber\n    {\n        get => _pageNumber;\n        set\n        {\n            if (value > 0)\n            {\n                _pageNumber = value;\n            }\n        }\n    }\n\n    private int _pageSize = 10;\n\n    public int PageSize\n    {\n        get => _pageSize;\n        set\n        {\n            if (value > 0)\n            {\n                _pageSize = value;\n            }\n        }\n    }\n\n    private int _totalCount;\n\n    public int TotalCount\n    {\n        get => _totalCount;\n        set\n        {\n            if (value > 0)\n            {\n                _totalCount = value;\n            }\n        }\n    }\n\n    public int PageCount => (_totalCount + _pageSize - 1) / _pageSize;\n\n    public T this[int index] => Data[index];\n\n    public int Count => Data.Count;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/PagedRequest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\npublic class PagedRequest\n{\n    private int _pageNumber = 1;\n    private int _pageSize = 10;\n\n    /// <summary>\n    /// PageNumber\n    /// 1 by default, 1 based\n    /// </summary>\n    public int PageNum\n    {\n        get => _pageNumber;\n        set\n        {\n            if (value > 0)\n            {\n                _pageNumber = value;\n            }\n        }\n    }\n\n    /// <summary>\n    /// PageSize\n    /// 10 by default\n    /// </summary>\n    public int PageSize\n    {\n        get => _pageSize;\n        set\n        {\n            if (value > 0)\n            {\n                _pageSize = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/Result.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\npublic record Result\n{\n    /// <summary>\n    /// ResultStatus\n    /// </summary>\n    public ResultStatus Status { get; set; }\n\n    /// <summary>\n    /// Message\n    /// </summary>\n    public string? Msg { get; set; }\n\n    public static Result Success(string? msg = null)\n    {\n        return new()\n        {\n            Status = ResultStatus.Success,\n            Msg = msg\n        };\n    }\n\n    public static Result<T> Success<T>(T result, string? msg = null)\n    {\n        return new()\n        {\n            Status = ResultStatus.Success,\n            Msg = msg,\n            Data = result\n        };\n    }\n\n    public static Result Fail(string? msg, ResultStatus status = ResultStatus.BadRequest)\n    {\n        return new()\n        {\n            Msg = msg,\n            Status = status,\n        };\n    }\n\n    public static Result<T> Fail<T>(string? msg, ResultStatus status = ResultStatus.BadRequest, T? result = default)\n    {\n        return new()\n        {\n            Msg = msg,\n            Status = status,\n            Data = result\n        };\n    }\n\n    public Result<T> ToResult<T>(T data)\n    {\n        return new Result<T>()\n        {\n            Data = data,\n            Status = Status,\n            Msg = Msg,\n        };\n    }\n}\n\npublic record Result<T> : Result\n{\n    public T? Data { get; set; }\n\n    public Result<T1> ToResult<T1>(Func<T?, T1> converter)\n    {\n        Guard.NotNull(converter);\n        return new()\n        {\n            Data = converter(Data),\n            Status = Status,\n            Msg = Msg,\n        };\n    }\n}\n\npublic static class ResultExtensions\n{\n    /// <summary>\n    /// Whether the result status is Success\n    /// </summary>\n    /// <param name=\"result\">result</param>\n    /// <returns>true, status is Success, otherwise not</returns>\n    public static bool IsSuccess(this Result result)\n        => Guard.NotNull(result).Status == ResultStatus.Success;\n\n    public static Result<T> WrapResult<T>(this T t, ResultStatus status = ResultStatus.Success, string? msg = null)\n        => new()\n        {\n            Data = t,\n            Status = status,\n            Msg = msg\n        };\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/ResultStatus.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.ComponentModel;\n\nnamespace WeihanLi.Common.Models;\n\npublic enum ResultStatus\n{\n    [Description(\"Empty Status\")]\n    None = 0,\n\n    [Description(\"Continue\")]\n    Continue = 100,\n\n    [Description(\"Processing\")]\n    Processing = 102,\n\n    [Description(\"Success\")]\n    Success = 200,\n\n    [Description(\"Created\")]\n    Created = 201,\n\n    [Description(\"Accepted\")]\n    Accepted = 202,\n\n    [Description(\"BadRequest, Request Parameter Error\")]\n    [Obsolete(\"Please use BadRequest instead\", true)]\n    RequestError = 400,\n\n    [Description(\"BadRequest\")]\n    BadRequest = 400,\n\n    [Description(\"Unauthorized\")]\n    Unauthorized = 401,\n\n    [Description(\"NoPermission\")]\n    [Obsolete(\"Please use Forbidden instead\")]\n    NoPermission = 403,\n\n    [Description(\"Forbidden\")]\n    Forbidden = 403,\n\n    [Description(\"ResourceNotFound\")]\n    [Obsolete(\"Please use NotFound instead\", true)]\n    ResourceNotFound = 404,\n\n    [Description(\"NotFound\")]\n    NotFound = 404,\n\n    [Description(\"MethodNotAllowed\")]\n    MethodNotAllowed = 405,\n\n    [Description(\"RequestTimeout\")]\n    RequestTimeout = 408,\n\n    [Description(\"TooManyRequests\")]\n    TooManyRequests = 429,\n\n    [Description(\"Process failed, Server Internal Error\")]\n    [Obsolete(\"Please use InternalError instead\", true)]\n    ProcessFail = 500,\n\n    [Description(\"InternalError\")]\n    InternalError = 500,\n\n    [Description(\"Not Implemented\")]\n    NotImplemented = 501,\n\n    [Description(\"ServiceUnavailable\")]\n    ServiceUnavailable = 503,\n\n    [Description(\"VersionNotSupported\")]\n    VersionNotSupported = 505\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/ReviewState.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\n/// <summary>\n/// ReviewState\n/// 审核状态\n/// </summary>\npublic enum ReviewState\n{\n    /// <summary>\n    /// UnReviewed\n    /// 待审核\n    /// </summary>\n    UnReviewed = 0,\n\n    /// <summary>\n    /// Reviewed\n    /// 审核通过\n    /// </summary>\n    Reviewed = 1,\n\n    /// <summary>\n    /// Rejected\n    /// 审核被拒绝\n    /// </summary>\n    Rejected = 2,\n}\n\npublic class ReviewRequest\n{\n    public ReviewState State { get; set; }\n\n    public string? Remark { get; set; }\n\n    public virtual bool IsValid()\n    {\n        if (State == ReviewState.Rejected && string.IsNullOrWhiteSpace(Remark))\n        {\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/SoftDeleteEntity.cs",
    "content": "﻿namespace WeihanLi.Common.Models;\n\npublic interface ISoftDeleteEntity;\n\npublic interface ISoftDeleteEntityWithDeleted : ISoftDeleteEntity\n{\n    bool IsDeleted { get; set; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/StringValueDictionary.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Models;\n\npublic sealed class StringValueDictionary : IEquatable<StringValueDictionary>\n{\n    private readonly Dictionary<string, string?> _dictionary = [];\n\n    private StringValueDictionary(IDictionary<string, string?> dictionary)\n    {\n        foreach (var pair in dictionary)\n        {\n            _dictionary[pair.Key] = pair.Value;\n        }\n    }\n\n    private StringValueDictionary(StringValueDictionary dictionary)\n    {\n        foreach (var key in dictionary.Keys)\n        {\n            _dictionary[key] = dictionary[key];\n        }\n    }\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used\")]\n    public static StringValueDictionary FromObject(object obj)\n    {\n        Guard.NotNull(obj);\n        if (obj is StringValueDictionary dictionary3)\n        {\n            return new StringValueDictionary(dictionary3);\n        }\n        if (obj is IDictionary<string, string?> dictionary)\n        {\n            return new StringValueDictionary(dictionary);\n        }\n        if (obj is IDictionary<string, object?> dictionary2)\n        {\n            return new StringValueDictionary(dictionary2.ToDictionary(p => p.Key, p => p.Value?.ToString()));\n        }\n        return new StringValueDictionary(obj.GetType().GetProperties()\n            .ToDictionary(p => p.Name, p => p.GetValue(obj)?.ToString()));\n    }\n\n    public static StringValueDictionary FromJson(string json)\n    {\n        Guard.NotNull(json, nameof(json));\n        var dic = json.JsonToObject<Dictionary<string, object?>>()\n            .ToDictionary(x => x.Key, x => x.Value?.ToString());\n        return new StringValueDictionary(dic);\n    }\n\n    public StringValueDictionary Clone() => new(this);\n\n    public int Count => _dictionary.Count;\n\n    public bool ContainsKey(string key) => _dictionary.ContainsKey(key) ? _dictionary.ContainsKey(key) : throw new ArgumentOutOfRangeException(nameof(key));\n\n    public string? this[string key] => _dictionary[key];\n\n    public Dictionary<string, string>.KeyCollection Keys => _dictionary.Keys!;\n\n    public bool Equals(StringValueDictionary? other)\n    {\n        if (other is null) return false;\n        if (other.Count != Count) return false;\n        foreach (var key in _dictionary.Keys)\n        {\n            if (!other.ContainsKey(key))\n            {\n                return false;\n            }\n            if (_dictionary[key] != other[key])\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public override bool Equals(object? obj)\n    {\n        return Equals(obj as StringValueDictionary);\n    }\n\n    public override int GetHashCode()\n    {\n        return string.Join(\"_\", _dictionary.Select(pair => $\"{pair.Key}={pair.Value}\")).GetHashCode();\n    }\n\n    public static bool operator ==(StringValueDictionary? current, StringValueDictionary? other)\n    {\n        return current?.Equals(other) == true;\n    }\n\n    public static bool operator !=(StringValueDictionary? current, StringValueDictionary? other)\n    {\n        return current?.Equals(other) != true;\n    }\n\n    public static implicit operator Dictionary<string, string?>(StringValueDictionary dictionary)\n    {\n        return dictionary._dictionary;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/TenantInfo.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\npublic class TenantInfo<TKey>\n{\n    public TKey? TenantId { get; set; }\n\n    public string? TenantName { get; set; }\n}\n\npublic class TenantInfo : TenantInfo<string>;\n"
  },
  {
    "path": "src/WeihanLi.Common/Models/ValidationResult.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Models;\n\npublic interface IValidationResult\n{\n    /// <summary>\n    /// Valid\n    /// </summary>\n    bool Valid { get; }\n\n    /// <summary>\n    /// ErrorMessages\n    /// Key: memberName\n    /// Value: errorMessages\n    /// </summary>\n    Dictionary<string, string[]> Errors { get; }\n}\n\npublic sealed class ValidationResult : IValidationResult\n{\n    private Dictionary<string, string[]> _errors = [];\n\n    /// <inheritdoc cref=\"IValidationResult\"/>\n    public bool Valid { get; set; }\n\n    /// <inheritdoc cref=\"IValidationResult\"/>\n    public Dictionary<string, string[]> Errors\n    {\n        get => _errors;\n        set => _errors = Guard.NotNull(value);\n    }\n\n    public static ValidationResult Failed(params string[] errors)\n    {\n        var result = new ValidationResult\n        {\n            Errors =\n            {\n                [string.Empty] = errors\n            }\n        };\n        return result;\n    }\n\n    public static ValidationResult Failed(Dictionary<string, string[]> errors)\n    {\n        var result = new ValidationResult\n        {\n            Errors = errors\n        };\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Otp/OtpHashAlgorithm.cs",
    "content": "﻿// ReSharper disable InconsistentNaming\nnamespace WeihanLi.Common.Otp;\n\npublic enum OtpHashAlgorithm\n{\n    /// <summary>\n    /// Sha1 is used as the HMAC hashing algorithm\n    /// </summary>\n    SHA1 = 0,\n\n    /// <summary>\n    /// Sha256 is used as the HMAC hashing algorithm\n    /// </summary>\n    SHA256 = 1,\n\n    /// <summary>\n    /// Sha512 is used as the HMAC hashing algorithm\n    /// </summary>\n    SHA512 = 2,\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Otp/Totp.cs",
    "content": "﻿using System.Security.Cryptography;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Otp;\n\n/// <summary>\n/// Time-Based One-Time Password\n/// https://datatracker.ietf.org/doc/html/rfc6238\n/// </summary>\npublic class Totp\n{\n    private readonly OtpHashAlgorithm _hashAlgorithm;\n    private readonly int _codeSize;\n    private readonly int _base;\n\n    /// <summary>\n    /// Create a totp instance with with default algorithm(SHA1 by default) and default code size(6 by default)\n    /// </summary>\n    public Totp() : this(OtpHashAlgorithm.SHA1)\n    {\n    }\n\n    /// <summary>\n    /// Create a totp instance with with default algorithm(SHA1 by default) and expected code size\n    /// </summary>\n    /// <param name=\"codeSize\">The expected code size, 6 by default, should between 1 and 9</param>\n    /// <exception cref=\"ArgumentOutOfRangeException\">Exception when codeSize invalid</exception>\n    public Totp(int codeSize) : this(OtpHashAlgorithm.SHA1, codeSize)\n    {\n    }\n\n    /// <summary>\n    /// Create a totp instance\n    /// </summary>\n    /// <param name=\"otpHashAlgorithm\">The hash algorithm to compute, SHA1 by default</param>\n    /// <param name=\"codeSize\">The expected code size, 6 by default, should between 1 and 9</param>\n    /// <exception cref=\"ArgumentOutOfRangeException\">Exception when codeSize invalid</exception>\n    public Totp(OtpHashAlgorithm otpHashAlgorithm, int codeSize = 6)\n    {\n        // valid input parameter\n        if (codeSize is <= 0 or >= 10)\n        {\n            throw new ArgumentOutOfRangeException(nameof(codeSize), codeSize, @\"The codeSize must between 1 and 9\");\n        }\n        _codeSize = codeSize;\n        _hashAlgorithm = otpHashAlgorithm;\n        _base = (int)Math.Pow(10, _codeSize);\n    }\n\n    /// <summary>\n    /// Compute totp\n    /// </summary>\n    /// <param name=\"securityToken\">base32 encoded token/secret</param>\n    /// <returns>computed totp code</returns>\n    public virtual string Compute(string securityToken) => Compute(Base32EncodeHelper.GetBytes(securityToken));\n\n    /// <summary>\n    /// Compute totp\n    /// </summary>\n    /// <param name=\"securityToken\">security token/secret</param>\n    /// <returns>computed totp code</returns>\n    public virtual string Compute(byte[] securityToken) => Compute(securityToken, GetCurrentTimeStepNumber());\n\n    /// <summary>\n    /// Compute totp with ttl\n    /// </summary>\n    /// <param name=\"securityToken\">security token/secret</param>\n    /// <returns>computed totp code and code ttl</returns>\n    public virtual (string Code, int Ttl) ComputeWithTtl(byte[] securityToken)\n    {\n        var currentStep = GetCurrentTimeStepNumber();\n        var ttl = Ttl(currentStep);\n        if (ttl < 1)\n        {\n            //going to be expired\n            currentStep++;\n            ttl = TimeStepSeconds;\n        }\n        var totp = Compute(securityToken, currentStep);\n        return (totp, ttl);\n    }\n\n    /// <summary>\n    /// Verify whether the input code is correct\n    /// </summary>\n    /// <param name=\"securityToken\">base32 encoded token/secret</param>\n    /// <param name=\"code\">The code to validate</param>\n    /// <param name=\"timeToleration\">The time that could be treated as valid</param>\n    /// <returns>whether the code is valid, <c>true</c> valid, otherwise invalid</returns>\n    public virtual bool Verify(string securityToken, string code, TimeSpan? timeToleration = null) => Verify(Base32EncodeHelper.GetBytes(securityToken), code, timeToleration);\n\n    /// <summary>\n    /// Verify whether the input code is correct\n    /// </summary>\n    /// <param name=\"securityToken\">base32 encoded token/secret</param>\n    /// <param name=\"code\">The code to validate</param>\n    /// <param name=\"timeToleration\">The time that could be treated as valid</param>\n    /// <returns>whether the code is valid, <c>true</c> valid, otherwise invalid</returns>\n    public virtual bool Verify(byte[] securityToken, string code, TimeSpan? timeToleration = null)\n    {\n        if (string.IsNullOrWhiteSpace(code))\n            return false;\n\n        if (code.Length != _codeSize)\n            return false;\n\n        var step = GetCurrentTimeStepNumber();\n        var futureStep = timeToleration is { TotalSeconds: > TimeStepSeconds }\n            ? Math.Min((int)(timeToleration.Value.TotalSeconds / TimeStepSeconds), MaxTimeSteps)\n            : 1;\n        for (var i = 0; i < futureStep; i++)\n        {\n            var totp = Compute(securityToken, step - i);\n            if (totp == code)\n            {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private string Compute(byte[] securityToken, long counter)\n    {\n        using HMAC hmac = _hashAlgorithm switch\n        {\n            OtpHashAlgorithm.SHA256 => new HMACSHA256(securityToken),\n            OtpHashAlgorithm.SHA512 => new HMACSHA512(securityToken),\n            _ => new HMACSHA1(securityToken)\n        };\n        var stepBytes = BitConverter.GetBytes(counter);\n        if (BitConverter.IsLittleEndian)\n        {\n            Array.Reverse(stepBytes); // need BigEndian\n        }\n        // See https://tools.ietf.org/html/rfc4226\n        var hashResult = hmac.ComputeHash(stepBytes);\n\n        var offset = hashResult[^1] & 0xf;\n        var p = $\"{hashResult[offset]:X2}{hashResult[offset + 1]:X2}{hashResult[offset + 2]:X2}{hashResult[offset + 3]:X2}\";\n        var num = Convert.ToInt64(p, 16) & 0x7FFFFFFF;\n        var code = (num % _base).ToString(\"\");\n        return code.PadLeft(_codeSize, '0');\n    }\n\n    /// <summary>\n    /// time step\n    /// 30s(Recommend)\n    /// </summary>\n    public const int TimeStepSeconds = 30;\n\n    /// <summary>\n    /// MaxTimeSteps\n    /// </summary>\n    public const int MaxTimeSteps = 20;\n\n    /// <summary>\n    /// MaxTimeStepSeconds\n    /// </summary>\n    public const int MaxTimeStepSeconds = TimeStepSeconds * MaxTimeSteps;\n\n    // More info: https://tools.ietf.org/html/rfc6238#section-4\n    private static long GetCurrentTimeStepNumber() => DateTimeOffset.UtcNow.ToUnixTimeSeconds() / TimeStepSeconds;\n\n    private static int Ttl(long step) => (int)((step + 1) * TimeStepSeconds - DateTimeOffset.UtcNow.ToUnixTimeSeconds());\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Otp/TotpOptions.cs",
    "content": "﻿using System.Text;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Otp;\n\npublic sealed class TotpOptions\n{\n    /// <summary>\n    /// 计算 code 的算法\n    /// The algorithm for calculating the totp code\n    /// </summary>\n    public OtpHashAlgorithm Algorithm { get; set; } = OtpHashAlgorithm.SHA1;\n\n    /// <summary>\n    /// 生成的 code 长度\n    /// The expected code length, 4-9 expected\n    /// </summary>\n    public int Size\n    {\n        get => _size;\n        set\n        {\n            if (value is > 9 or < 4)\n                throw new ArgumentOutOfRangeException(nameof(value), value, @\"Size out of range, allowed range 4~9\");\n            _size = value;\n        }\n    }\n\n    /// <summary>\n    /// 过期时间，单位是秒\n    /// The code expire time, 300s by default, should be 30,60,90..., the min value is 30s\n    /// </summary>\n    public int ExpiresIn { get; set; } = 300;\n\n    private string? _salt;\n    private int _size = 6;\n\n    /// <summary>\n    /// Salt for security consideration\n    /// </summary>\n    public string? Salt\n    {\n        get => _salt;\n        set\n        {\n            _salt = value;\n            SaltBytes = value.IsNullOrEmpty() ? null : Encoding.UTF8.GetBytes(value);\n        }\n    }\n\n    internal byte[]? SaltBytes { get; private set; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Polyfill/TaskCompletionSource.cs",
    "content": "﻿#if !NET5_0_OR_GREATER\n// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nusing System.Collections.Generic;\n\n// ReSharper disable once CheckNamespace\nnamespace System.Threading.Tasks;\n\n/// <summary>\n/// Represents the producer side of a <see cref=\"Tasks.Task\"/> unbound to a\n/// delegate, providing access to the consumer side through the <see cref=\"Tasks.Task\"/> property.\n/// </summary>\n/// <remarks>\n/// <para>\n/// It is often the case that a <see cref=\"Tasks.Task\"/> is desired to\n/// represent another asynchronous operation.\n/// <see cref=\"TaskCompletionSource\">TaskCompletionSource</see> is provided for this purpose. It enables\n/// the creation of a task that can be handed out to consumers, and those consumers can use the members\n/// of the task as they would any other. However, unlike most tasks, the state of a task created by a\n/// TaskCompletionSource is controlled explicitly by the methods on TaskCompletionSource. This enables the\n/// completion of the external asynchronous operation to be propagated to the underlying Task. The\n/// separation also ensures that consumers are not able to transition the state without access to the\n/// corresponding TaskCompletionSource.\n/// </para>\n/// <para>\n/// All members of <see cref=\"TaskCompletionSource\"/> are thread-safe\n/// and may be used from multiple threads concurrently.\n/// </para>\n/// </remarks>\ninternal sealed class TaskCompletionSource\n{\n    private readonly TaskCompletionSource<object?> _taskCompletionSource;\n\n    /// <summary>Creates a <see cref=\"TaskCompletionSource\"/>.</summary>\n    public TaskCompletionSource() => _taskCompletionSource = new();\n\n\n    /// <summary>Creates a <see cref=\"TaskCompletionSource\"/> with the specified options.</summary>\n    /// <remarks>\n    /// The <see cref=\"Tasks.Task\"/> created by this instance and accessible through its <see cref=\"Task\"/> property\n    /// will be instantiated using the specified <paramref name=\"creationOptions\"/>.\n    /// </remarks>\n    /// <param name=\"creationOptions\">The options to use when creating the underlying <see cref=\"Tasks.Task\"/>.</param>\n    /// <exception cref=\"ArgumentOutOfRangeException\">\n    /// The <paramref name=\"creationOptions\"/> represent options invalid for use\n    /// with a <see cref=\"TaskCompletionSource\"/>.\n    /// </exception>\n    public TaskCompletionSource(TaskCreationOptions creationOptions) :\n        this(null, creationOptions)\n    {\n    }\n\n    /// <summary>Creates a <see cref=\"TaskCompletionSource\"/> with the specified state.</summary>\n    /// <param name=\"state\">The state to use as the underlying\n    /// <see cref=\"Tasks.Task\"/>'s AsyncState.</param>\n    public TaskCompletionSource(object? state) :\n        this(state, TaskCreationOptions.None)\n    {\n    }\n\n    /// <summary>Creates a <see cref=\"TaskCompletionSource\"/> with the specified state and options.</summary>\n    /// <param name=\"creationOptions\">The options to use when creating the underlying <see cref=\"Tasks.Task\"/>.</param>\n    /// <param name=\"state\">The state to use as the underlying <see cref=\"Tasks.Task\"/>'s AsyncState.</param>\n    /// <exception cref=\"ArgumentOutOfRangeException\">The <paramref name=\"creationOptions\"/> represent options invalid for use with a <see cref=\"TaskCompletionSource\"/>.</exception>\n    public TaskCompletionSource(object? state, TaskCreationOptions creationOptions) =>\n        _taskCompletionSource = new(state, creationOptions);\n\n    /// <summary>\n    /// Gets the <see cref=\"Tasks.Task\"/> created\n    /// by this <see cref=\"TaskCompletionSource\"/>.\n    /// </summary>\n    /// <remarks>\n    /// This property enables a consumer access to the <see cref=\"Task\"/> that is controlled by this instance.\n    /// The <see cref=\"SetResult\"/>, <see cref=\"SetException(Exception)\"/>, <see cref=\"SetException(IEnumerable{Exception})\"/>,\n    /// and <see cref=\"SetCanceled\"/> methods (and their \"Try\" variants) on this instance all result in the relevant state\n    /// transitions on this underlying Task.\n    /// </remarks>\n    public Task Task => _taskCompletionSource.Task;\n\n    /// <summary>Transitions the underlying <see cref=\"Tasks.Task\"/> into the <see cref=\"TaskStatus.Faulted\"/> state.</summary>\n    /// <param name=\"exception\">The exception to bind to this <see cref=\"Tasks.Task\"/>.</param>\n    /// <exception cref=\"ArgumentNullException\">The <paramref name=\"exception\"/> argument is null.</exception>\n    /// <exception cref=\"InvalidOperationException\">\n    /// The underlying <see cref=\"Tasks.Task\"/> is already in one of the three final states:\n    /// <see cref=\"TaskStatus.RanToCompletion\"/>,\n    /// <see cref=\"TaskStatus.Faulted\"/>, or\n    /// <see cref=\"TaskStatus.Canceled\"/>.\n    /// </exception>\n    public void SetException(Exception exception) => _taskCompletionSource.SetException(exception);\n\n    /// <summary>Transitions the underlying <see cref=\"Tasks.Task\"/> into the <see cref=\"TaskStatus.Faulted\"/> state.</summary>\n    /// <param name=\"exceptions\">The collection of exceptions to bind to this <see cref=\"Tasks.Task\"/>.</param>\n    /// <exception cref=\"ArgumentNullException\">The <paramref name=\"exceptions\"/> argument is null.</exception>\n    /// <exception cref=\"ArgumentException\">There are one or more null elements in <paramref name=\"exceptions\"/>.</exception>\n    /// <exception cref=\"InvalidOperationException\">\n    /// The underlying <see cref=\"Tasks.Task\"/> is already in one of the three final states:\n    /// <see cref=\"TaskStatus.RanToCompletion\"/>,\n    /// <see cref=\"TaskStatus.Faulted\"/>, or\n    /// <see cref=\"TaskStatus.Canceled\"/>.\n    /// </exception>\n    public void SetException(IEnumerable<Exception> exceptions) => _taskCompletionSource.SetException(exceptions);\n\n    /// <summary>\n    /// Attempts to transition the underlying <see cref=\"Tasks.Task\"/> into the <see cref=\"TaskStatus.Faulted\"/> state.\n    /// </summary>\n    /// <param name=\"exception\">The exception to bind to this <see cref=\"Tasks.Task\"/>.</param>\n    /// <returns>True if the operation was successful; otherwise, false.</returns>\n    /// <remarks>\n    /// This operation will return false if the <see cref=\"Tasks.Task\"/> is already in one of the three final states:\n    /// <see cref=\"TaskStatus.RanToCompletion\"/>,\n    /// <see cref=\"TaskStatus.Faulted\"/>, or\n    /// <see cref=\"TaskStatus.Canceled\"/>.\n    /// </remarks>\n    /// <exception cref=\"ArgumentNullException\">The <paramref name=\"exception\"/> argument is null.</exception>\n    public bool TrySetException(Exception exception) => _taskCompletionSource.TrySetException(exception);\n\n    /// <summary>\n    /// Attempts to transition the underlying <see cref=\"Tasks.Task\"/> into the <see cref=\"TaskStatus.Faulted\"/> state.\n    /// </summary>\n    /// <param name=\"exceptions\">The collection of exceptions to bind to this <see cref=\"Tasks.Task\"/>.</param>\n    /// <returns>True if the operation was successful; otherwise, false.</returns>\n    /// <remarks>\n    /// This operation will return false if the <see cref=\"Tasks.Task\"/> is already in one of the three final states:\n    /// <see cref=\"TaskStatus.RanToCompletion\"/>,\n    /// <see cref=\"TaskStatus.Faulted\"/>, or\n    /// <see cref=\"TaskStatus.Canceled\"/>.\n    /// </remarks>\n    /// <exception cref=\"ArgumentNullException\">The <paramref name=\"exceptions\"/> argument is null.</exception>\n    /// <exception cref=\"ArgumentException\">There are one or more null elements in <paramref name=\"exceptions\"/>.</exception>\n    /// <exception cref=\"ArgumentException\">The <paramref name=\"exceptions\"/> collection is empty.</exception>\n    public bool TrySetException(IEnumerable<Exception> exceptions) => _taskCompletionSource.TrySetException(exceptions);\n\n    /// <summary>\n    /// Transitions the underlying <see cref=\"Tasks.Task\"/> into the <see cref=\"TaskStatus.RanToCompletion\"/> state.\n    /// </summary>\n    /// <exception cref=\"InvalidOperationException\">\n    /// The underlying <see cref=\"Tasks.Task\"/> is already in one of the three final states:\n    /// <see cref=\"TaskStatus.RanToCompletion\"/>,\n    /// <see cref=\"TaskStatus.Faulted\"/>, or\n    /// <see cref=\"TaskStatus.Canceled\"/>.\n    /// </exception>\n    public void SetResult() => _taskCompletionSource.SetResult(null);\n\n    /// <summary>\n    /// Attempts to transition the underlying <see cref=\"Tasks.Task\"/> into the <see cref=\"TaskStatus.RanToCompletion\"/> state.\n    /// </summary>\n    /// <returns>True if the operation was successful; otherwise, false.</returns>\n    /// <remarks>\n    /// This operation will return false if the <see cref=\"Tasks.Task\"/> is already in one of the three final states:\n    /// <see cref=\"TaskStatus.RanToCompletion\"/>,\n    /// <see cref=\"TaskStatus.Faulted\"/>, or\n    /// <see cref=\"TaskStatus.Canceled\"/>.\n    /// </remarks>\n    public bool TrySetResult() => _taskCompletionSource.TrySetResult(null);\n\n    /// <summary>\n    /// Transitions the underlying <see cref=\"Tasks.Task\"/> into the <see cref=\"TaskStatus.Canceled\"/> state.\n    /// </summary>\n    /// <exception cref=\"InvalidOperationException\">\n    /// The underlying <see cref=\"Tasks.Task\"/> is already in one of the three final states:\n    /// <see cref=\"TaskStatus.RanToCompletion\"/>,\n    /// <see cref=\"TaskStatus.Faulted\"/>, or\n    /// <see cref=\"TaskStatus.Canceled\"/>.\n    /// </exception>\n    public void SetCanceled() => _taskCompletionSource.SetCanceled();\n\n    /// <summary>\n    /// Attempts to transition the underlying <see cref=\"Tasks.Task\"/> into the <see cref=\"TaskStatus.Canceled\"/> state.\n    /// </summary>\n    /// <returns>True if the operation was successful; otherwise, false.</returns>\n    /// <remarks>\n    /// This operation will return false if the <see cref=\"Tasks.Task\"/> is already in one of the three final states:\n    /// <see cref=\"TaskStatus.RanToCompletion\"/>,\n    /// <see cref=\"TaskStatus.Faulted\"/>, or\n    /// <see cref=\"TaskStatus.Canceled\"/>.\n    /// </remarks>\n    public bool TrySetCanceled() => TrySetCanceled(default);\n\n    /// <summary>\n    /// Attempts to transition the underlying <see cref=\"Tasks.Task\"/> into the <see cref=\"TaskStatus.Canceled\"/> state.\n    /// </summary>\n    /// <param name=\"cancellationToken\">The cancellation token with which to cancel the <see cref=\"Tasks.Task\"/>.</param>\n    /// <returns>True if the operation was successful; otherwise, false.</returns>\n    /// <remarks>\n    /// This operation will return false if the <see cref=\"Tasks.Task\"/> is already in one of the three final states:\n    /// <see cref=\"TaskStatus.RanToCompletion\"/>,\n    /// <see cref=\"TaskStatus.Faulted\"/>, or\n    /// <see cref=\"TaskStatus.Canceled\"/>.\n    /// </remarks>\n    public bool TrySetCanceled(CancellationToken cancellationToken) => _taskCompletionSource.TrySetCanceled(cancellationToken);\n}\n\n#endif\n"
  },
  {
    "path": "src/WeihanLi.Common/README.md",
    "content": "# WeihanLi.Common\n\n## Intro\n\n`WeihanLi.Common` is a common library based on .NETStandard2.0\n\n## Features\n\n- Dependence Injection\n- Fluent Aspects -- AOP implement\n- Event Related(EventBus/EventQueue/EventStore)\n- Logging Framework\n- Dapper-like Ado.Net extensions\n- TOTP implement\n- and more ...\n"
  },
  {
    "path": "src/WeihanLi.Common/Resource.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace WeihanLi.Common {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"16.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resource {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resource() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"WeihanLi.Common.Resource\", typeof(Resource).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to 参数不合法.\n        /// </summary>\n        internal static string InvalidArgument {\n            get {\n                return ResourceManager.GetString(\"InvalidArgument\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to 参数类型不合法，参数类型不能是{0}.\n        /// </summary>\n        internal static string InvalidArgumentType {\n            get {\n                return ResourceManager.GetString(\"InvalidArgumentType\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to 操作失败.\n        /// </summary>\n        internal static string InvokeFail {\n            get {\n                return ResourceManager.GetString(\"InvokeFail\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to LogHelper 尚未初始化，请先初始化 LogHelper.\n        /// </summary>\n        internal static string LogHelperNotInitialized {\n            get {\n                return ResourceManager.GetString(\"LogHelperNotInitialized\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to {0} must be lambda expression.\n        /// </summary>\n        internal static string propertyExpression_must_be_lambda_expression {\n            get {\n                return ResourceManager.GetString(\"propertyExpression_must_be_lambda_expression\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Task类型不能序列化.\n        /// </summary>\n        internal static string TaskCanNotBeSerialized {\n            get {\n                return ResourceManager.GetString(\"TaskCanNotBeSerialized\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to 不支持的LogHelperLevel.\n        /// </summary>\n        internal static string UnSupportedLogHelperLevel {\n            get {\n                return ResourceManager.GetString(\"UnSupportedLogHelperLevel\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to value must be positive.\n        /// </summary>\n        internal static string ValueMustBePositive {\n            get {\n                return ResourceManager.GetString(\"ValueMustBePositive\", resourceCulture);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Resource.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"InvalidArgument\" xml:space=\"preserve\">\n    <value>参数不合法</value>\n    <comment>参数不合法</comment>\n  </data>\n  <data name=\"InvalidArgumentType\" xml:space=\"preserve\">\n    <value>参数类型不合法，参数类型不能是{0}</value>\n    <comment>参数类型不合法</comment>\n  </data>\n  <data name=\"InvokeFail\" xml:space=\"preserve\">\n    <value>操作失败</value>\n    <comment>操作失败</comment>\n  </data>\n  <data name=\"LogHelperNotInitialized\" xml:space=\"preserve\">\n    <value>LogHelper 尚未初始化，请先初始化 LogHelper</value>\n    <comment>LogHelper未初始化</comment>\n  </data>\n  <data name=\"TaskCanNotBeSerialized\" xml:space=\"preserve\">\n    <value>Task类型不能序列化</value>\n  </data>\n  <data name=\"UnSupportedLogHelperLevel\" xml:space=\"preserve\">\n    <value>不支持的LogHelperLevel</value>\n  </data>\n  <data name=\"propertyExpression_must_be_lambda_expression\" xml:space=\"preserve\">\n    <value>{0} must be lambda expression</value>\n  </data>\n  <data name=\"ValueMustBePositive\" xml:space=\"preserve\">\n    <value>value must be positive</value>\n  </data>\n</root>"
  },
  {
    "path": "src/WeihanLi.Common/Services/CancellationTokenProvider.cs",
    "content": "﻿namespace WeihanLi.Common.Services;\n\npublic interface ICancellationTokenProvider\n{\n    CancellationToken GetCancellationToken();\n}\n\npublic sealed class NullCancellationTokenProvider : ICancellationTokenProvider\n{\n    public CancellationToken GetCancellationToken() => CancellationToken.None;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Services/IProfiler.cs",
    "content": "﻿using System.Diagnostics;\n\nnamespace WeihanLi.Common.Services;\n\npublic interface IProfiler\n{\n    /// <summary>Starts, or resumes, measuring elapsed time for an interval.</summary>\n    void Start();\n\n    /// <summary>Stops measuring elapsed time for an interval.</summary>\n    void Stop();\n\n    /// <summary>\n    /// Stops time interval measurement, resets the elapsed time to zero, and starts measuring elapsed time.\n    /// </summary>\n    void Restart();\n\n    /// <summary>\n    /// Gets the total elapsed time measured by the current instance, in milliseconds.\n    /// </summary>\n    TimeSpan Elapsed { get; }\n}\n\npublic sealed class StopwatchProfiler : IProfiler\n{\n    private readonly Stopwatch _stopwatch = new();\n\n    public void Start()\n    {\n        _stopwatch.Start();\n    }\n\n    public void Stop()\n    {\n        _stopwatch.Stop();\n    }\n\n    public void Restart()\n    {\n        _stopwatch.Restart();\n    }\n\n    public TimeSpan Elapsed => _stopwatch.Elapsed;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Services/IScope.cs",
    "content": "﻿namespace WeihanLi.Common.Services;\n\npublic interface IScope : IDisposable;\n\npublic sealed class NullScope : IScope\n{\n    public void Dispose()\n    {\n    }\n\n    public static NullScope Instance { get; } = new();\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Services/ITenantProvider.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Common.Models;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Services;\n\npublic interface ITenantProvider\n{\n    string? GetTenantId();\n\n    TenantInfo? GetTenantInfo();\n}\n\npublic static class TenantIdProviderExtensions\n{\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T? GetTenantId<T>(this ITenantProvider tenantIdProvider, T? defaultValue = default)\n    {\n        return tenantIdProvider.GetTenantId().ToOrDefault(defaultValue);\n    }\n\n    [RequiresUnreferencedCode(\"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static bool TryGetTenantId<T>(this ITenantProvider tenantIdProvider, out T? value, T? defaultValue = default)\n    {\n        try\n        {\n            var tenantId = tenantIdProvider.GetTenantId();\n            if (!string.IsNullOrEmpty(tenantId))\n            {\n                value = tenantId.To<T>();\n                return true;\n            }\n        }\n        catch (Exception)\n        {\n            // ignored\n        }\n\n        value = defaultValue;\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Services/IdGenerator.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Services;\n\n/// <summary>\n/// IdGenerator\n/// </summary>\npublic interface IIdGenerator\n{\n    /// <summary>\n    /// Generate a new id\n    /// </summary>\n    /// <returns>new id</returns>\n    string NewId();\n}\n\n/// <summary>\n/// IdGenerator based on Guid\n/// </summary>\npublic sealed class GuidIdGenerator : IIdGenerator\n{\n    public static GuidIdGenerator Instance { get; } = new();\n\n    public string NewId() => Guid.NewGuid().ToString(\"N\");\n}\n\npublic sealed class SequentialGuidIdGenerator(SequentialGuidType sequentialGuidType) : IIdGenerator\n{\n    private readonly SequentialGuidType _sequentialGuidType = sequentialGuidType;\n\n    public string NewId() => SequentialGuidGenerator.Create(_sequentialGuidType).ToString(\"N\");\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Services/TotpService.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Options;\nusing System.Text;\nusing WeihanLi.Common.Otp;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Services;\n\n/// <summary>\n/// Time-based one-time password service\n/// </summary>\npublic interface ITotpService\n{\n    string GetCode(byte[] securityToken);\n\n    (string Code, int Ttl) GetCodeWithTtl(byte[] securityToken);\n    bool VerifyCode(byte[] securityToken, string code);\n}\n\npublic static class TotpServiceExtensions\n{\n    public static string GetCode(this ITotpService totpService, string securityToken, Encoding? encoding = null)\n    {\n        Guard.NotNull(totpService);\n        Guard.NotNullOrEmpty(securityToken);\n        return totpService.GetCode(securityToken.GetBytes(encoding));\n    }\n\n    public static (string Code, int Ttl) GetCodeWithTtl(this ITotpService totpService, string securityToken, Encoding? encoding = null)\n    {\n        Guard.NotNull(totpService);\n        Guard.NotNullOrEmpty(securityToken);\n        return totpService.GetCodeWithTtl(securityToken.GetBytes(encoding));\n    }\n\n    public static bool VerifyCode(this ITotpService totpService, string securityToken, string code, Encoding? encoding = null)\n    {\n        Guard.NotNull(totpService);\n        Guard.NotNullOrEmpty(securityToken);\n        return totpService.VerifyCode(securityToken.GetBytes(encoding), code);\n    }\n}\n\npublic interface ITotpServiceFactory\n{\n    ITotpService GetService(string? name = null);\n}\n\npublic sealed class TotpServiceFactory(IOptionsMonitor<TotpOptions> optionsMonitor) : ITotpServiceFactory\n{\n    private readonly IOptionsMonitor<TotpOptions> _optionsMonitor = optionsMonitor;\n\n    public ITotpService GetService(string? name = null)\n    {\n        return new TotpService(_optionsMonitor.Get(name ?? Options.DefaultName));\n    }\n}\n\npublic sealed class TotpService : ITotpService\n{\n    private readonly TotpOptions _totpOptions;\n    private readonly TimeSpan? _expiry;\n    private readonly Totp _totp;\n\n    public TotpService(TotpOptions totpOptions)\n    {\n        _totpOptions = Guard.NotNull(totpOptions);\n        _expiry = totpOptions.ExpiresIn is >= Totp.TimeStepSeconds * 2 and <= Totp.MaxTimeStepSeconds\n            ? TimeSpan.FromSeconds(totpOptions.ExpiresIn)\n            : null;\n        _totp = new Totp(_totpOptions.Algorithm, _totpOptions.Size);\n    }\n\n    public string GetCode(byte[] securityToken)\n    {\n        Guard.NotNull(securityToken, nameof(securityToken));\n\n        if (_totpOptions.SaltBytes.IsNullOrEmpty())\n            return _totp.Compute(securityToken);\n\n        var bytes = new byte[securityToken.Length + _totpOptions.SaltBytes.Length];\n        Array.Copy(securityToken, bytes, securityToken.Length);\n        Array.Copy(_totpOptions.SaltBytes, 0, bytes, securityToken.Length, _totpOptions.SaltBytes.Length);\n        return _totp.Compute(bytes);\n    }\n\n    public (string Code, int Ttl) GetCodeWithTtl(byte[] securityToken)\n    {\n        Guard.NotNull(securityToken, nameof(securityToken));\n\n        if (_totpOptions.SaltBytes.IsNullOrEmpty())\n            return _totp.ComputeWithTtl(securityToken);\n\n        var bytes = new byte[securityToken.Length + _totpOptions.SaltBytes.Length];\n        Array.Copy(securityToken, bytes, securityToken.Length);\n        Array.Copy(_totpOptions.SaltBytes, 0, bytes, securityToken.Length, _totpOptions.SaltBytes.Length);\n        return _totp.ComputeWithTtl(bytes);\n    }\n\n    public bool VerifyCode(byte[] securityToken, string code)\n    {\n        if (string.IsNullOrEmpty(code) || code.Length != _totpOptions.Size) return false;\n        Guard.NotNull(securityToken);\n\n        if (_totpOptions.SaltBytes.IsNullOrEmpty())\n            return _totp.Verify(securityToken, code, _expiry);\n\n        var bytes = new byte[securityToken.Length + _totpOptions.SaltBytes.Length];\n        Array.Copy(securityToken, bytes, securityToken.Length);\n        Array.Copy(_totpOptions.SaltBytes, 0, bytes, securityToken.Length, _totpOptions.SaltBytes.Length);\n        return _totp.Verify(bytes, code, _expiry);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Services/UserIdProvider.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Services;\n\npublic interface IUserIdProvider\n{\n    string? GetUserId();\n}\n\npublic static class UserIdProviderExtensions\n{\n    [RequiresUnreferencedCode(\n        \"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static T? GetUserId<T>(this IUserIdProvider userIdProvider, T? defaultValue = default)\n    {\n        return userIdProvider.GetUserId().ToOrDefault(defaultValue);\n    }\n\n    [RequiresUnreferencedCode(\n        \"Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.\")]\n    public static bool TryGetUserId<T>(this IUserIdProvider userIdProvider, out T? value, T? defaultValue = default)\n    {\n        try\n        {\n            var userId = userIdProvider.GetUserId();\n            if (!string.IsNullOrEmpty(userId))\n            {\n                value = userId.To<T>();\n                return true;\n            }\n        }\n        catch (Exception)\n        {\n            // ignored\n        }\n\n        value = defaultValue;\n        return false;\n    }\n}\n\npublic class EnvironmentUserIdProvider : IUserIdProvider\n{\n    public static EnvironmentUserIdProvider Instance { get; } = new();\n\n    public virtual string GetUserId() => Environment.UserName;\n}\n\npublic sealed class DelegateUserIdProvider(Func<string?> userIdFactory) : IUserIdProvider\n{\n    public DelegateUserIdProvider(string userId) : this(() => userId)\n    {\n    }\n\n    public string? GetUserId() => userIdFactory();\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Services/Validator.cs",
    "content": "﻿using System.ComponentModel.DataAnnotations;\nusing System.Diagnostics.CodeAnalysis;\nusing WeihanLi.Extensions;\nusing AnnotationValidationResult = System.ComponentModel.DataAnnotations.ValidationResult;\nusing ValidationResult = WeihanLi.Common.Models.ValidationResult;\n\nnamespace WeihanLi.Common.Services;\n\npublic interface IValidator\n{\n    ValidationResult Validate(object? value);\n}\n\npublic interface IValidator<in T>\n{\n    ValidationResult Validate(T value);\n}\n\npublic interface IAsyncValidator<in T>\n{\n    Task<ValidationResult> ValidateAsync(T value);\n}\n\npublic sealed class DataAnnotationValidator : IValidator\n{\n    public static IValidator Instance { get; } = new DataAnnotationValidator();\n\n    [RequiresUnreferencedCode(\"Unreferenced code may be used.\")]\n    public ValidationResult Validate(object? value)\n    {\n        var validationResult = new ValidationResult();\n        if (value is null)\n        {\n            validationResult.Valid = false;\n            validationResult.Errors ??= [];\n            validationResult.Errors[string.Empty] = [\"Value is null\"];\n        }\n        else\n        {\n            var annotationValidateResults = new List<AnnotationValidationResult>();\n            validationResult.Valid =\n                Validator.TryValidateObject(value, new ValidationContext(value), annotationValidateResults);\n            validationResult.Errors = annotationValidateResults\n                .GroupBy(x => x.MemberNames.StringJoin(\",\"))\n                .ToDictionary(g => g.Key, g => g.Select(x => x.ErrorMessage).WhereNotNull().ToArray());\n        }\n        return validationResult;\n    }\n}\n\npublic sealed class DelegateValidator(Func<object?, ValidationResult> validateFunc) : IValidator\n{\n    private readonly Func<object?, ValidationResult> _validateFunc = Guard.NotNull(validateFunc);\n\n    public ValidationResult Validate(object? value)\n    {\n        return _validateFunc.Invoke(value);\n    }\n}\n\npublic sealed class DelegateValidator<T> : IValidator<T>, IAsyncValidator<T>\n{\n    private readonly Func<T, Task<ValidationResult>> _validateFunc;\n\n    public DelegateValidator(Func<T, ValidationResult> validateFunc)\n    {\n        Guard.NotNull(validateFunc);\n        _validateFunc = t => validateFunc(t).WrapTask();\n    }\n\n    public DelegateValidator(Func<T, Task<ValidationResult>> validateFunc)\n    {\n        _validateFunc = Guard.NotNull(validateFunc);\n    }\n\n    public ValidationResult Validate(T value)\n    {\n        return _validateFunc.Invoke(value).GetAwaiter().GetResult();\n    }\n\n    public Task<ValidationResult> ValidateAsync(T value)\n    {\n        return _validateFunc.Invoke(value);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Services/Wrapper.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Services;\n\npublic interface IWrapper<out T>\n{\n    T Value { get; }\n}\n\npublic class Wrapper<T>(T value) : IWrapper<T>\n{\n    public T Value { get; } = value;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/ConfigurationRenderMiddleare.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Configuration;\n\nnamespace WeihanLi.Common.Template;\n\ninternal sealed class ConfigurationRenderMiddleware(IConfiguration? configuration = null)\n    : IRenderMiddleware\n{\n    private const string Prefix = \"$config\";\n    public Task InvokeAsync(TemplateRenderContext context, Func<TemplateRenderContext, Task> next)\n    {\n        if (configuration is not null)\n        {\n            foreach (var pair in context.Inputs\n                         .Where(x => x.Key.Prefix is Prefix\n                                     && x.Value is null)\n                    )\n            {\n                context.Inputs[pair.Key] = configuration[pair.Key.VariableName];\n            }\n        }\n\n        return next(context);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/DefaultRenderMiddleware.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Template;\n\ninternal sealed class DefaultRenderMiddleware(Dictionary<string, ITemplatePipe> pipes) : IRenderMiddleware\n{\n    public async Task InvokeAsync(TemplateRenderContext context, Func<TemplateRenderContext, Task> next)\n    {\n        await next(context);\n        foreach (var input in context.Inputs.Keys)\n        {\n            var value = context.Inputs[input];\n            if (input.Pipes is { Length: > 0 })\n            {\n                foreach (var pipeInput in input.Pipes)\n                {\n                    if (pipes.TryGetValue(pipeInput.PipeName, out var pipe))\n                    {\n                        value = pipe.Convert(value, pipeInput.Arguments);\n                    }\n                }\n            }\n            // replace input with value\n            context.RenderedText = context.RenderedText.Replace(input.Input, value as string ?? value?.ToString());\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/DefaultTemplateParser.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Text.RegularExpressions;\n\nnamespace WeihanLi.Common.Template;\n\ninternal sealed class DefaultTemplateParser : ITemplateParser\n{\n    private const string VariableGroupRegexExp = @\"\\{\\{(?<Variable>[\\w\\$\\s:\\.]+)(?<Pipe>|[^\\{\\}]*)\\}\\}\";\n    private static readonly Regex VariableRegex = new(VariableGroupRegexExp, RegexOptions.Compiled);\n    public Task<TemplateRenderContext> ParseAsync(string text)\n    {\n        List<TemplateInput> inputs = [];\n        var match = VariableRegex.Match(text);\n        while (match.Success)\n        {\n            var pipes = Array.Empty<TemplatePipeInput>();\n            var variableInput = match.Groups[\"Variable\"].Value;\n            var variableName = variableInput.Trim();\n            string? prefix = null;\n\n            var prefixIndex = variableName.IndexOf('$'); // prefix start\n            if (prefixIndex >= 0)\n            {\n                var nameIndex = variableName.IndexOf(' ', prefixIndex); // name start\n                prefix = variableName[..nameIndex].Trim();\n                variableName = variableName[nameIndex..].Trim();\n            }\n\n            var pipeValue = match.Groups[\"Pipe\"]?.Value.Trim();\n            if (!string.IsNullOrEmpty(pipeValue))\n            {\n                var pipeIndex = pipeValue!.IndexOf('|');\n                if (pipeIndex < 0)\n                {\n                    match = match.NextMatch();\n                    continue;\n                }\n\n                // exact pipes\n                pipeValue = pipeValue[pipeIndex..].Trim();\n                var pipeInputs = pipeValue.Split(['|'], StringSplitOptions.RemoveEmptyEntries);\n                pipes = pipeInputs.Select(p =>\n                    {\n                        var pipeName = p.Trim();\n                        var arguments = Array.Empty<string>();\n                        var sep = pipeName.IndexOf(':');\n                        if (sep >= 0)\n                        {\n                            if (sep + 1 < pipeName.Length)\n                            {\n                                var argumentsText = pipeName[(sep + 1)..].Trim();\n                                arguments =\n#if NET\n                                    argumentsText.Split([':'],\n                                        StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);\n#else\n                                argumentsText.Split([':'], StringSplitOptions.RemoveEmptyEntries)\n                                    .Select(x => x.Trim()).ToArray();\n#endif\n                            }\n\n                            pipeName = pipeName[..sep].Trim();\n                        }\n\n                        return new TemplatePipeInput() { PipeName = pipeName, Arguments = arguments };\n                    }).ToArray();\n            }\n\n            var input = new TemplateInput\n            {\n                Input = match.Value,\n                Prefix = prefix,\n                VariableName = variableName,\n                Pipes = pipes\n            };\n            inputs.Add(input);\n\n            match = match.NextMatch();\n        }\n        var context = new TemplateRenderContext(text, inputs);\n        return Task.FromResult(context);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/DefaultTemplateRenderer.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Template;\n\ninternal sealed class DefaultTemplateRenderer(Func<TemplateRenderContext, Task> renderFunc)\n    : ITemplateRenderer\n{\n    public async Task<string> RenderAsync(TemplateRenderContext context, object? globals)\n    {\n        if (context.Text.IsNullOrWhiteSpace() || context.Inputs.IsNullOrEmpty())\n            return context.Text;\n\n        var parameters = globals.ParseParamDictionary();\n        if (parameters is { Count: > 0 })\n        {\n            foreach (var input in context.Inputs.Keys.Where(x => x.Prefix is null))\n            {\n                if (parameters.TryGetValue(input.VariableName, out var value))\n                {\n                    context.Inputs[input] = value;\n                }\n            }\n        }\n        await renderFunc.Invoke(context).ConfigureAwait(false);\n        return context.RenderedText;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/DependencyInjectionExtensions.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.DependencyInjection.Extensions;\nusing Microsoft.Extensions.Options;\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Template;\n\npublic static class DependencyInjectionExtensions\n{\n    public static ITemplateEngineServiceBuilder AddTemplateEngine(\n        this IServiceCollection services,\n        Action<TemplateEngineOptions>? optionsConfigure = null\n        )\n    {\n        Guard.NotNull(services);\n        if (services.Any(x => x.ServiceType == typeof(ITemplateEngine)))\n            throw new InvalidOperationException(\"Template engine services had been registered\");\n\n        if (optionsConfigure != null)\n            services.AddOptions().Configure(optionsConfigure);\n\n        services.TryAddSingleton<ITemplateEngine, TemplateEngine>();\n        services.TryAddSingleton<ITemplateParser, DefaultTemplateParser>();\n        services.TryAddSingleton<ITemplateRenderer, DefaultTemplateRenderer>();\n\n        var serviceBuilder = new TemplateEngineServiceBuilder(services);\n\n        serviceBuilder.AddPipe<TextFormatTemplatePipe>()\n            .AddPipe<UpperCaseTemplatePipe>()\n            .AddPipe<LowerCaseTemplatePipe>()\n            .AddPipe<TitleCaseTemplatePipe>()\n            ;\n\n        serviceBuilder.AddRenderMiddleware(sp =>\n            {\n                var pipes = sp.GetServices<ITemplatePipe>().ToDictionary(p => p.Name, p => p);\n                return new DefaultRenderMiddleware(pipes);\n            })\n            .AddRenderMiddleware<EnvRenderMiddleware>()\n            .AddRenderMiddleware(sp =>\n            {\n                var configuration = sp.GetService<IOptions<TemplateEngineOptions>>()?.Value.Configuration\n                    ?? sp.GetService<IConfiguration>();\n                return new ConfigurationRenderMiddleware(configuration);\n            })\n            ;\n\n        services.AddSingleton(sp =>\n        {\n            var pipelineBuilder = PipelineBuilder.CreateAsync<TemplateRenderContext>();\n            foreach (var middleware in sp.GetServices<IRenderMiddleware>())\n            {\n                pipelineBuilder.UseMiddleware(middleware);\n            }\n            return pipelineBuilder.Build();\n        });\n\n        return serviceBuilder;\n    }\n\n    public static ITemplateEngineServiceBuilder AddRenderMiddleware<TMiddleware>(\n        this ITemplateEngineServiceBuilder serviceBuilder,\n        Func<IServiceProvider, TMiddleware>? middlewareFactory = null\n        ) where TMiddleware : class, IRenderMiddleware\n    {\n        var serviceDescriptor = middlewareFactory is null\n                ? ServiceDescriptor.Singleton<IRenderMiddleware, TMiddleware>()\n                : ServiceDescriptor.Singleton<IRenderMiddleware>(middlewareFactory)\n            ;\n        serviceBuilder.Services.Add(serviceDescriptor);\n        return serviceBuilder;\n    }\n\n    public static ITemplateEngineServiceBuilder AddPipe<TPipe>\n        (this ITemplateEngineServiceBuilder serviceBuilder)\n        where TPipe : class, ITemplatePipe\n    {\n        serviceBuilder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ITemplatePipe, TPipe>());\n        return serviceBuilder;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/EnvRenderMiddleware.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Template;\n\ninternal sealed class EnvRenderMiddleware : IRenderMiddleware\n{\n    private const string Prefix = \"$env\";\n    public Task InvokeAsync(TemplateRenderContext context, Func<TemplateRenderContext, Task> next)\n    {\n        foreach (var pair in context.Inputs\n                     .Where(x => x.Key.Prefix is Prefix\n                         && x.Value is null)\n                 )\n        {\n            var variable = pair.Key.VariableName;\n            context.Inputs[pair.Key] = Environment.GetEnvironmentVariable(variable);\n        }\n        return next(context);\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/IRenderMiddleware.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Template;\n\npublic interface IRenderMiddleware : IAsyncPipelineMiddleware<TemplateRenderContext>;\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/ITemplateEngine.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Template;\n\npublic interface ITemplateEngine\n{\n    Task<string> RenderAsync(string text, object? globals = null);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/ITemplateParser.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Template;\n\npublic interface ITemplateParser\n{\n    Task<TemplateRenderContext> ParseAsync(string text);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/ITemplatePipe.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Template;\n\npublic interface ITemplatePipe\n{\n    string Name { get; }\n    object? Convert(object? value, params ReadOnlySpan<string> args);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/ITemplateRenderer.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Template;\n\npublic interface ITemplateRenderer\n{\n    Task<string> RenderAsync(TemplateRenderContext template, object? globals);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/ITemplateRendererBuilder.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Template;\n\npublic interface ITemplateRendererBuilder\n{\n    ITemplateRendererBuilder UseTemplatePipe<TPipe>(TPipe pipe)\n        where TPipe : class, ITemplatePipe;\n\n    ITemplateRendererBuilder UseRenderMiddleware<TMiddleware>(TMiddleware middleware)\n        where TMiddleware : class, IRenderMiddleware;\n}\n\npublic interface ITemplateEngineBuilder : ITemplateRendererBuilder\n{\n    ITemplateRendererBuilder ConfigureOptions(Action<TemplateEngineOptions> optionsConfigure);\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/TemplateEngine.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nnamespace WeihanLi.Common.Template;\n\npublic sealed class TemplateEngine(ITemplateParser templateParser, ITemplateRenderer templateRenderer)\n    : ITemplateEngine, ITemplateRenderer, ITemplateParser\n{\n    private readonly ITemplateParser _templateParser = templateParser;\n    private readonly ITemplateRenderer _templateRenderer = templateRenderer;\n\n    public Task<TemplateRenderContext> ParseAsync(string text)\n    {\n        return _templateParser.ParseAsync(text);\n    }\n    public async Task<string> RenderAsync(TemplateRenderContext context, object? globals)\n    {\n        return await _templateRenderer.RenderAsync(context, globals);\n    }\n\n    public async Task<string> RenderAsync(string text, object? parameters = null)\n    {\n        var context = await _templateParser.ParseAsync(text);\n        var result = await _templateRenderer.RenderAsync(context, parameters);\n        return result;\n    }\n\n    public static TemplateEngine CreateDefault(Action<ITemplateEngineBuilder>? builderConfigure = null)\n    {\n        var builder = new TemplateEngineBuilder();\n        builder.UseTemplatePipe(new TextFormatTemplatePipe());\n        builder.UseTemplatePipe(new UpperCaseTemplatePipe());\n        builder.UseTemplatePipe(new LowerCaseTemplatePipe());\n        builder.UseTemplatePipe(new TitleCaseTemplatePipe());\n        builderConfigure?.Invoke(builder);\n        return new TemplateEngine(builder.BuildParser(), builder.BuildRenderer());\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/TemplateEngineOptions.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Configuration;\n\nnamespace WeihanLi.Common.Template;\n\npublic sealed class TemplateEngineOptions\n{\n    public IConfiguration? Configuration { get; set; }\n\n    internal Dictionary<string, ITemplatePipe> Pipes { get; init; } = new();\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/TemplateEngineServiceBuilder.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace WeihanLi.Common.Template;\n\npublic interface ITemplateEngineServiceBuilder\n{\n    IServiceCollection Services { get; }\n}\n\ninternal sealed class TemplateEngineServiceBuilder(IServiceCollection services)\n    : ITemplateEngineServiceBuilder\n{\n    public IServiceCollection Services { get; } = services;\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/TemplatePipes.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Globalization;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Template;\n\npublic abstract class TemplatePipeBase : ITemplatePipe\n{\n    protected virtual int? ParameterCount => 1;\n\n    public abstract string Name { get; }\n    public object? Convert(object? value, params ReadOnlySpan<string> args)\n    {\n        if (ParameterCount.HasValue && ParameterCount.Value != args.Length)\n        {\n            throw new InvalidOperationException($\"The number of arguments {args.Length} must be equal to the parameter count({ParameterCount}).\");\n        }\n\n        return ConvertInternal(value, args);\n    }\n\n    protected abstract object? ConvertInternal(object? value, params ReadOnlySpan<string> args);\n}\n\npublic sealed class TextFormatTemplatePipe : TemplatePipeBase\n{\n    protected override object? ConvertInternal(object? value, params ReadOnlySpan<string> args)\n        => FormatText(value, args[0]);\n\n    public override string Name => \"format\";\n    private string? FormatText(object? value, string format)\n    {\n        return value switch\n        {\n            null => null,\n            IFormattable text => text.ToString(format, CultureInfo.InvariantCulture),\n            _ => value.ToString()\n        };\n    }\n}\n\npublic abstract class TextTransformTemplatePipe : TemplatePipeBase\n{\n    protected override int? ParameterCount => 0;\n\n    protected override object? ConvertInternal(object? value, params ReadOnlySpan<string> args)\n    {\n        var str = value as string ?? value?.ToString();\n        return str is null ? null : ConvertText(str);\n    }\n\n    protected abstract string? ConvertText(string value);\n}\n\npublic sealed class UpperCaseTemplatePipe : TextTransformTemplatePipe\n{\n    public override string Name => \"toUpper\";\n    protected override string ConvertText(string value)\n    {\n        return value.ToUpperInvariant();\n    }\n}\n\npublic sealed class LowerCaseTemplatePipe : TextTransformTemplatePipe\n{\n    public override string Name => \"toLower\";\n    protected override string ConvertText(string value)\n    {\n        return value.ToLowerInvariant();\n    }\n}\n\npublic sealed class TitleCaseTemplatePipe : TextTransformTemplatePipe\n{\n    public override string Name => \"toTitle\";\n    protected override string ConvertText(string value)\n    {\n        return value.ToTitleCase();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/TemplateRenderContext.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing System.Diagnostics;\nusing WeihanLi.Common.Abstractions;\n\nnamespace WeihanLi.Common.Template;\n\npublic sealed class TemplateRenderContext(string text, IReadOnlyCollection<TemplateInput> inputs)\n    : IProperties\n{\n    public string Text { get; } = text;\n    public Dictionary<TemplateInput, object?> Inputs { get; } =\n        inputs.ToDictionary(x => x, _ => (object?)null);\n    public string RenderedText { get; set; } = text;\n    public IDictionary<string, object?> Properties { get; } = new Dictionary<string, object?>();\n}\n\n[DebuggerDisplay(\"{Input,nq}\")]\npublic sealed class TemplateInput : IEquatable<TemplateInput>\n{\n    public required string Input { get; init; }\n    public required string? Prefix { get; init; }\n    public required string VariableName { get; init; }\n    public required TemplatePipeInput[] Pipes { get; init; }\n    public bool Equals(TemplateInput? other) => other is not null && other.Input == Input;\n    public override bool Equals(object? obj) => obj is TemplateInput input && Equals(input);\n    public override int GetHashCode() => Input.GetHashCode();\n}\n\n[DebuggerDisplay(\"{PipeName,nq}\")]\npublic sealed class TemplatePipeInput\n{\n    public required string PipeName { get; init; }\n    public required string[] Arguments { get; init; }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/Template/TemplateRendererBuilder.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Helpers;\n\nnamespace WeihanLi.Common.Template;\n\ninternal sealed class TemplateEngineBuilder : ITemplateEngineBuilder\n{\n    private readonly IAsyncPipelineBuilder<TemplateRenderContext> _pipelineBuilder =\n        PipelineBuilder.CreateAsync<TemplateRenderContext>();\n    private Action<TemplateEngineOptions>? _optionsConfigure;\n    private readonly Dictionary<string, ITemplatePipe> _pipes = new();\n\n    public ITemplateRendererBuilder UseTemplatePipe<TPipe>(TPipe pipe) where TPipe : class, ITemplatePipe\n    {\n        _pipes[pipe.Name] = pipe;\n        return this;\n    }\n\n    public ITemplateRendererBuilder UseRenderMiddleware<TMiddleware>(TMiddleware middleware) where TMiddleware : class, IRenderMiddleware\n    {\n        _pipelineBuilder.UseMiddleware(Guard.NotNull(middleware));\n        return this;\n    }\n\n    public ITemplateRendererBuilder ConfigureOptions(Action<TemplateEngineOptions> optionsConfigure)\n    {\n        _optionsConfigure = Guard.NotNull(optionsConfigure);\n        return this;\n    }\n\n    public ITemplateParser BuildParser() => new DefaultTemplateParser();\n\n    public ITemplateRenderer BuildRenderer()\n    {\n        var options = new TemplateEngineOptions\n        {\n            Pipes = _pipes\n        };\n        _optionsConfigure?.Invoke(options);\n\n        _pipelineBuilder\n            .UseMiddleware(new DefaultRenderMiddleware(options.Pipes))\n            .UseMiddleware(new EnvRenderMiddleware())\n            .UseMiddleware(new ConfigurationRenderMiddleware(options.Configuration))\n            ;\n        return new DefaultTemplateRenderer(_pipelineBuilder.Build());\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common/WeihanLi.Common.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>            \n    <PackageId>WeihanLi.Common</PackageId>\n    <Title>WeihanLi.Common</Title>\n    <TargetFrameworks>netstandard2.0;net8.0;net9.0;net10.0;net11.0</TargetFrameworks>\n    <Description>common library,extensions helpers and useful utilities</Description>\n    <PackageTags>$(PackageTags);common;utility;lib;library;extensions;helpers</PackageTags>\n    <PackageSummary>common library,extensions helpers and useful utilities</PackageSummary>\n    <PackageProjectUrl>https://github.com/WeihanLi/WeihanLi.Common/tree/dev/src/WeihanLi.Common</PackageProjectUrl>\n    <NoWarn>$(NoWarn);1591;DE0003;SYSLIB0014;</NoWarn>\n  </PropertyGroup>\n  <PropertyGroup>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <!-- https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions?view=vs-2022#msbuild-targetframework-and-targetplatform-functions -->\n    <!-- <IsAotCompatible>$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))</IsAotCompatible> -->\n    <!-- DynamicallyAccessedMembers not included by default https://github.com/Sergio0694/PolySharp/issues/84#issuecomment-1751772472 -->\n    <PolySharpIncludeRuntimeSupportedAttributes>true</PolySharpIncludeRuntimeSupportedAttributes>\n  </PropertyGroup>  \n  <ItemGroup Condition=\"'$(TargetFramework)' == 'netstandard2.0'\">\n    <PackageReference Include=\"System.Reflection.Emit\" />\n    <PackageReference Include=\"System.ComponentModel.Annotations\" />\n    <PackageReference Include=\"Microsoft.CSharp\" PrivateAssets=\"All\" />\n    <Compile Remove=\"Helpers\\InMemoryStream.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.Extensions.Configuration\" />\n    <PackageReference Include=\"Microsoft.Extensions.Logging\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Newtonsoft.Json\" />\n  </ItemGroup>\n  <ItemGroup>\n    <AssemblyAttribute Include=\"System.CLSCompliantAttribute\">\n      <_Parameter1>true</_Parameter1>\n    </AssemblyAttribute>\n    <Using Include=\"System.Diagnostics.CodeAnalysis\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Service Include=\"{508349b6-6b84-4df5-91f0-309beebad82d}\" />\n    <Compile Update=\"DependencyInjection\\ServiceContainerBuilderExtensions.generated.cs\">\n      <DesignTime>True</DesignTime>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>ServiceContainerBuilderExtensions.tt</DependentUpon>\n    </Compile>\n    <Compile Update=\"Extensions\\DbCommandExtension.generated.cs\">\n      <DesignTime>True</DesignTime>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>DbCommandExtension.tt</DependentUpon>\n    </Compile>\n    <None Update=\"DependencyInjection\\ServiceContainerBuilderExtensions.tt\">\n      <Generator>TextTemplatingFileGenerator</Generator>\n      <LastGenOutput>ServiceContainerBuilderExtensions.generated.cs</LastGenOutput>\n    </None>\n    <None Update=\"Extensions\\DbCommandExtension.tt\">\n      <Generator>TextTemplatingFileGenerator</Generator>\n      <LastGenOutput>DbCommandExtension.generated.cs</LastGenOutput>\n    </None>\n    <Compile Update=\"Extensions\\DbConnectionExtension.generated.cs\">\n      <DesignTime>True</DesignTime>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>DbConnectionExtension.tt</DependentUpon>\n    </Compile>\n    <None Update=\"Extensions\\DbConnectionExtension.tt\">\n      <Generator>TextTemplatingFileGenerator</Generator>\n      <LastGenOutput>DbConnectionExtension.generated.cs</LastGenOutput>\n    </None>\n    <Compile Update=\"Resource.Designer.cs\">\n      <DesignTime>True</DesignTime>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Resource.resx</DependentUpon>\n    </Compile>\n    <EmbeddedResource Update=\"Resource.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resource.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "src/WeihanLi.Common.Logging.Serilog/README.md",
    "content": "﻿# WeihanLi.Common.Logging.Serilog [![WeihanLi.Common.Logging.Serilog](https://img.shields.io/nuget/v/WeihanLi.Common.Logging.Serilog.svg)](https://www.nuget.org/packages/WeihanLi.Common.Logging.Serilog/)\n\n## Intro\n\n- serilog extensions\n\n  - SerilogHelper\n\n- `SerilogLoggerProvider`for Microsoft.Extensions.Logging\n\n- `SerilogLogHelperProvider` for WeihanLi.Common.Logging\n"
  },
  {
    "path": "src/WeihanLi.Common.Logging.Serilog/SerilogHelper.cs",
    "content": "﻿using Serilog;\n\nnamespace WeihanLi.Common.Logging.Serilog;\n\npublic static class SerilogHelper\n{\n    private static readonly Lock Locker = new();\n\n    public static void LogInit(Action<LoggerConfiguration> configureAction)\n    {\n        Guard.NotNull(configureAction);\n        var loggerConfiguration = new LoggerConfiguration();\n        loggerConfiguration.Enrich.FromLogContext();\n        configureAction(loggerConfiguration);\n        LogInit(loggerConfiguration);\n    }\n\n    public static void LogInit(LoggerConfiguration loggerConfiguration)\n    {\n        lock (Locker)\n        {\n            Log.Logger = loggerConfiguration.CreateLogger();\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common.Logging.Serilog/SerilogLogHelperProvider.cs",
    "content": "﻿using Serilog;\nusing Serilog.Events;\nusing Serilog.Parsing;\nusing SSerilog = Serilog;\n\nnamespace WeihanLi.Common.Logging.Serilog;\n\ninternal sealed class SerilogLogHelperProvider : ILogHelperProvider, IDisposable\n{\n    private static readonly MessageTemplateParser _messageTemplateParser = new();\n\n    public SerilogLogHelperProvider(LoggerConfiguration configuration)\n    {\n        SerilogHelper.LogInit(configuration);\n    }\n\n    public SerilogLogHelperProvider(Action<LoggerConfiguration> configurationAction)\n    {\n        SerilogHelper.LogInit(configurationAction);\n    }\n\n    public void Dispose()\n    {\n        SSerilog.Log.CloseAndFlush();\n    }\n\n    public void Log(LogHelperLoggingEvent loggingEvent)\n    {\n        var logger = SSerilog.Log.ForContext(SourceContextPropName, loggingEvent.CategoryName);\n\n        var logLevel = GetSerilogEventLevel(loggingEvent.LogLevel);\n        if (logger.IsEnabled(logLevel))\n        {\n            var messageTemplate = loggingEvent.MessageTemplate;\n            var properties = new List<LogEventProperty>();\n            if (loggingEvent.Properties != null)\n            {\n                foreach (var property in loggingEvent.Properties)\n                {\n                    if (logger.BindProperty(property.Key, property.Value, false, out var bound))\n                        properties.Add(bound);\n                }\n            }\n            var parsedTemplate = _messageTemplateParser.Parse(messageTemplate ?? \"\");\n            logger.Write(new LogEvent(loggingEvent.DateTime, logLevel, loggingEvent.Exception, parsedTemplate, properties));\n        }\n    }\n\n    private static LogEventLevel GetSerilogEventLevel(LogHelperLogLevel logHelperLevel)\n    {\n        return logHelperLevel switch\n        {\n            LogHelperLogLevel.All => LogEventLevel.Verbose,\n            LogHelperLogLevel.Debug => LogEventLevel.Debug,\n            LogHelperLogLevel.Info => LogEventLevel.Information,\n            LogHelperLogLevel.Trace => LogEventLevel.Debug,\n            LogHelperLogLevel.Warn => LogEventLevel.Warning,\n            LogHelperLogLevel.Error => LogEventLevel.Error,\n            LogHelperLogLevel.Fatal => LogEventLevel.Fatal,\n            LogHelperLogLevel.None => LogEventLevel.Fatal,\n            _ => LogEventLevel.Warning,\n        };\n    }\n\n    private const string SourceContextPropName = \"SourceContext\";\n}\n\npublic static class LogHelperFactoryExtensions\n{\n    public static ILogHelperLoggingBuilder AddSerilog(this ILogHelperLoggingBuilder loggingBuilder, Action<LoggerConfiguration> loggerConfigurationAction)\n    {\n        loggingBuilder.AddProvider(new SerilogLogHelperProvider(loggerConfigurationAction));\n        return loggingBuilder;\n    }\n\n    public static ILogHelperLoggingBuilder AddSerilog(this ILogHelperLoggingBuilder loggingBuilder,\n        LoggerConfiguration loggerConfiguration)\n    {\n        loggingBuilder.AddProvider(new SerilogLogHelperProvider(loggerConfiguration));\n        return loggingBuilder;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common.Logging.Serilog/SerilogLogger.cs",
    "content": "﻿// Copyright (c) .NET Foundation. All rights reserved.\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\n\nusing Microsoft.Extensions.Logging;\nusing Serilog.Core;\nusing Serilog.Events;\nusing Serilog.Parsing;\nusing System.Reflection;\nusing FrameworkLogger = Microsoft.Extensions.Logging.ILogger;\n\n// ReSharper disable once CheckNamespace\nnamespace Serilog.Extensions.Logging;\n\ninternal sealed class SerilogLogger : FrameworkLogger\n{\n    private readonly SerilogLoggerProvider _provider;\n    private readonly ILogger _logger;\n\n    private static readonly MessageTemplateParser _messageTemplateParser = new();\n\n    public SerilogLogger(\n        SerilogLoggerProvider provider,\n        ILogger? logger = null,\n        string? name = null)\n    {\n        _provider = WeihanLi.Common.Guard.NotNull(provider);\n\n        // If a logger was passed, the provider has already added itself as an enricher\n        _logger = logger ?? Serilog.Log.Logger.ForContext(new[] { provider });\n\n        if (name != null)\n        {\n            _logger = _logger.ForContext(Constants.SourceContextPropertyName, name);\n        }\n    }\n\n    public bool IsEnabled(LogLevel logLevel)\n    {\n        return _logger.IsEnabled(ConvertLevel(logLevel));\n    }\n\n    public IDisposable? BeginScope<TState>(TState state) where TState : notnull\n    {\n        return _provider.BeginScope(state);\n    }\n\n    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string>? formatter)\n    {\n        var level = ConvertLevel(logLevel);\n        if (!_logger.IsEnabled(level))\n        {\n            return;\n        }\n\n        var logger = _logger;\n        string? messageTemplate = null;\n\n        var properties = new List<LogEventProperty>();\n\n        if (state is IEnumerable<KeyValuePair<string, object>> structure)\n        {\n            foreach (var property in structure)\n            {\n                if (property.Key == SerilogLoggerProvider.OriginalFormatPropertyName && property.Value is string)\n                {\n                    messageTemplate = (string)property.Value;\n                }\n                else if (property.Key.StartsWith(\"@\"))\n                {\n                    if (logger.BindProperty(property.Key[1..], property.Value, true, out var destructured))\n                        properties.Add(destructured);\n                }\n                else\n                {\n                    if (logger.BindProperty(property.Key, property.Value, false, out var bound))\n                        properties.Add(bound);\n                }\n            }\n\n            var stateType = state.GetType();\n            var stateTypeInfo = stateType.GetTypeInfo();\n            // Imperfect, but at least eliminates `1 names\n            if (messageTemplate == null && !stateTypeInfo.IsGenericType)\n            {\n                messageTemplate = \"{\" + stateType.Name + \":l}\";\n                if (logger.BindProperty(stateType.Name, AsLoggableValue(state, formatter), false, out var stateTypeProperty))\n                    properties.Add(stateTypeProperty);\n            }\n        }\n\n        if (messageTemplate == null)\n        {\n            string? propertyName = null;\n            if (state != null)\n            {\n                propertyName = \"State\";\n                messageTemplate = \"{State:l}\";\n            }\n            else if (formatter != null)\n            {\n                propertyName = \"Message\";\n                messageTemplate = \"{Message:l}\";\n            }\n\n            if (propertyName != null)\n            {\n                if (logger.BindProperty(propertyName, AsLoggableValue(state, formatter), false, out var property))\n                    properties.Add(property);\n            }\n        }\n\n        if (eventId.Id != 0 || eventId.Name != null)\n            properties.Add(CreateEventIdProperty(eventId));\n\n        var parsedTemplate = _messageTemplateParser.Parse(messageTemplate ?? \"\");\n        var evt = new LogEvent(DateTimeOffset.Now, level, exception, parsedTemplate, properties);\n        logger.Write(evt);\n    }\n\n    private static object? AsLoggableValue<TState>(TState state, Func<TState, Exception?, string>? formatter)\n    {\n        object? sobj = state;\n        if (formatter != null)\n            sobj = formatter(state, null);\n        return sobj;\n    }\n\n    private static LogEventLevel ConvertLevel(LogLevel logLevel)\n    {\n        return logLevel switch\n        {\n            LogLevel.Critical => LogEventLevel.Fatal,\n            LogLevel.Error => LogEventLevel.Error,\n            LogLevel.Warning => LogEventLevel.Warning,\n            LogLevel.Information => LogEventLevel.Information,\n            LogLevel.Debug => LogEventLevel.Debug,\n            // ReSharper disable once RedundantCaseLabel\n            _ => LogEventLevel.Verbose,\n        };\n    }\n\n    private static LogEventProperty CreateEventIdProperty(EventId eventId)\n    {\n        var properties = new List<LogEventProperty>(2);\n\n        if (eventId.Id != 0)\n        {\n            properties.Add(new LogEventProperty(\"Id\", new ScalarValue(eventId.Id)));\n        }\n\n        if (eventId.Name != null)\n        {\n            properties.Add(new LogEventProperty(\"Name\", new ScalarValue(eventId.Name)));\n        }\n\n        return new LogEventProperty(\"EventId\", new StructureValue(properties));\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common.Logging.Serilog/SerilogLoggerExtensions.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\nusing Serilog;\nusing Serilog.Extensions.Logging;\nusing ILogger = Serilog.ILogger;\n\nnamespace WeihanLi.Common.Logging.Serilog;\n\npublic static class SerilogLoggerExtensions\n{\n    /// <summary>\n    /// Add Serilog to the logging pipeline.\n    /// </summary>\n    /// <param name=\"factory\">The logger factory to configure.</param>\n    /// <param name=\"logger\">The Serilog logger; if not supplied, the static <see cref=\"Log\"/> will be used.</param>\n    /// <param name=\"dispose\">When true, dispose <paramref name=\"logger\"/> when the framework disposes the provider. If the\n    /// logger is not specified but <paramref name=\"dispose\"/> is true, the <see cref=\"Log.CloseAndFlush()\"/> method will be\n    /// called on the static <see cref=\"Log\"/> class instead.</param>\n    /// <returns>The logger factory.</returns>\n    public static ILoggerFactory AddSerilog(\n        this ILoggerFactory factory,\n        ILogger? logger = null,\n        bool dispose = false)\n    {\n        Guard.NotNull(factory);\n\n        factory.AddProvider(new SerilogLoggerProvider(logger, dispose));\n\n        return factory;\n    }\n\n    /// <summary>\n    /// Add Serilog to the logging pipeline.\n    /// </summary>\n    /// <param name=\"builder\">The <see cref=\"T:Microsoft.Extensions.Logging.ILoggingBuilder\" /> to add logging provider to.</param>\n    /// <param name=\"logger\">The Serilog logger; if not supplied, the static <see cref=\"Log\"/> will be used.</param>\n    /// <param name=\"dispose\">When true, dispose <paramref name=\"logger\"/> when the framework disposes the provider. If the\n    /// logger is not specified but <paramref name=\"dispose\"/> is true, the <see cref=\"Log.CloseAndFlush()\"/> method will be\n    /// called on the static <see cref=\"Log\"/> class instead.</param>\n    /// <returns>The logging builder.</returns>\n    public static ILoggingBuilder AddSerilog(this ILoggingBuilder builder, ILogger? logger = null, bool dispose = false)\n    {\n        Guard.NotNull(builder);\n\n        if (dispose)\n        {\n            builder.Services.AddSingleton<ILoggerProvider, SerilogLoggerProvider>(_ => new SerilogLoggerProvider(logger, true));\n        }\n        else\n        {\n            builder.AddProvider(new SerilogLoggerProvider(logger));\n        }\n\n        return builder;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common.Logging.Serilog/SerilogLoggerProvider.cs",
    "content": "﻿// Copyright(c) .NET Foundation.All rights reserved.\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\n\nusing Microsoft.Extensions.Logging;\nusing Serilog.Context;\nusing Serilog.Core;\nusing Serilog.Events;\nusing FrameworkLogger = Microsoft.Extensions.Logging.ILogger;\n\n// ReSharper disable once CheckNamespace\nnamespace Serilog.Extensions.Logging;\n\n/// <summary>\n/// An <see cref=\"ILoggerProvider\"/> that pipes events through Serilog.\n/// </summary>\n[ProviderAlias(\"Serilog\")]\ninternal sealed class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher\n{\n    internal const string OriginalFormatPropertyName = \"{OriginalFormat}\";\n    internal const string ScopePropertyName = \"Scope\";\n\n    // May be null; if it is, Log.Logger will be lazily used\n    private readonly ILogger? _logger;\n\n    private readonly Action? _dispose;\n\n    /// <summary>\n    /// Construct a <see cref=\"SerilogLoggerProvider\"/>.\n    /// </summary>\n    /// <param name=\"logger\">A Serilog logger to pipe events through; if null, the static <see cref=\"Log\"/> class will be used.</param>\n    /// <param name=\"dispose\">If true, the provided logger or static log class will be disposed/closed when the provider is disposed.</param>\n    public SerilogLoggerProvider(ILogger? logger = null, bool dispose = false)\n    {\n        if (logger != null)\n            _logger = logger.ForContext(new[] { this });\n\n        if (dispose)\n        {\n            if (logger != null)\n                _dispose = () => (logger as IDisposable)?.Dispose();\n            else\n                _dispose = Log.CloseAndFlush;\n        }\n    }\n\n    public FrameworkLogger CreateLogger(string categoryName)\n    {\n        return new SerilogLogger(this, _logger, categoryName);\n    }\n\n    public IDisposable BeginScope<T>(T state)\n    {\n        WeihanLi.Common.Guard.NotNull(state);\n\n        if (CurrentScope != null)\n            return new SerilogLoggerScope(this, state);\n\n        // The outermost scope pushes and pops the Serilog `LogContext` - once\n        // this enricher is on the stack, the `CurrentScope` property takes care\n        // of the rest of the `BeginScope()` stack.\n        var popSerilogContext = LogContext.Push(this);\n        return new SerilogLoggerScope(this, state, popSerilogContext);\n    }\n\n    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)\n    {\n        List<LogEventPropertyValue>? scopeItems = null;\n        for (var scope = CurrentScope; scope != null; scope = scope.Parent)\n        {\n            scope.EnrichAndCreateScopeItem(logEvent, propertyFactory, out var scopeItem);\n\n            if (scopeItem != null)\n            {\n                scopeItems ??= [];\n                scopeItems.Add(scopeItem);\n            }\n        }\n\n        if (scopeItems != null)\n        {\n            scopeItems.Reverse();\n            logEvent.AddPropertyIfAbsent(new LogEventProperty(ScopePropertyName, new SequenceValue(scopeItems)));\n        }\n    }\n\n    private readonly AsyncLocal<SerilogLoggerScope?> _value = new();\n\n    internal SerilogLoggerScope? CurrentScope\n    {\n        get => _value.Value;\n        set => _value.Value = value;\n    }\n\n    public void Dispose()\n    {\n        _dispose?.Invoke();\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common.Logging.Serilog/SerilogLoggerScope.cs",
    "content": "﻿// Copyright (c) .NET Foundation. All rights reserved.\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\n\nusing Serilog.Core;\nusing Serilog.Events;\n\n// ReSharper disable once CheckNamespace\nnamespace Serilog.Extensions.Logging;\n\ninternal sealed class SerilogLoggerScope : IDisposable\n{\n    private const string NoName = \"None\";\n\n    private readonly SerilogLoggerProvider _provider;\n    private readonly object? _state;\n    private readonly IDisposable? _chainedDisposable;\n\n    // An optimization only, no problem if there are data races on this.\n    private bool _disposed;\n\n    public SerilogLoggerScope(SerilogLoggerProvider provider, object? state, IDisposable? chainedDisposable = null)\n    {\n        _provider = provider;\n        _state = state;\n\n        Parent = _provider.CurrentScope;\n        _provider.CurrentScope = this;\n        _chainedDisposable = chainedDisposable;\n    }\n\n    public SerilogLoggerScope? Parent { get; }\n\n    public void Dispose()\n    {\n        if (!_disposed)\n        {\n            _disposed = true;\n\n            // In case one of the parent scopes has been disposed out-of-order, don't\n            // just blindly reinstate our own parent.\n            for (var scan = _provider.CurrentScope; scan != null; scan = scan.Parent)\n            {\n                if (ReferenceEquals(scan, this))\n                    _provider.CurrentScope = Parent;\n            }\n\n            _chainedDisposable?.Dispose();\n        }\n    }\n\n    public void EnrichAndCreateScopeItem(LogEvent logEvent, ILogEventPropertyFactory propertyFactory, out LogEventPropertyValue? scopeItem)\n    {\n        if (_state == null)\n        {\n            scopeItem = null;\n            return;\n        }\n\n        if (_state is IEnumerable<KeyValuePair<string, object>> stateProperties)\n        {\n            scopeItem = null; // Unless it's `FormattedLogValues`, these are treated as property bags rather than scope items.\n\n            foreach (var stateProperty in stateProperties)\n            {\n                if (stateProperty.Key == SerilogLoggerProvider.OriginalFormatPropertyName && stateProperty.Value is string)\n                {\n                    scopeItem = new ScalarValue(_state.ToString());\n                    continue;\n                }\n\n                var key = stateProperty.Key;\n                var destructureObject = false;\n\n                if (key.StartsWith(\"@\"))\n                {\n                    key = key[1..];\n                    destructureObject = true;\n                }\n\n                var property = propertyFactory.CreateProperty(key, stateProperty.Value, destructureObject);\n                logEvent.AddPropertyIfAbsent(property);\n            }\n        }\n        else\n        {\n            scopeItem = propertyFactory.CreateProperty(NoName, _state).Value;\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Common.Logging.Serilog/WeihanLi.Common.Logging.Serilog.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFrameworks>netstandard2.0;net8.0;</TargetFrameworks>\n    <PackageProjectUrl>https://github.com/WeihanLi/WeihanLi.Common/tree/dev/src/WeihanLi.Common.Logging.Serilog</PackageProjectUrl>\n    <PackageLicenseExpression>MIT</PackageLicenseExpression>\n    <PackageTags>$(PackageTags);logging;serilog</PackageTags>\n    <Title>WeihanLi.Common.Logging.Serilog</Title>\n    <Description>serilog logging provider for WeihanLi.Common.Logging, and serilog extensions, see details https://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common.Logging.Serilog/README.md</Description>\n    <PackageSummary>serilog logging provider for WeihanLi.Common.Logging, and serilog extensions</PackageSummary>\n    <PackageReleaseNotes>\n      https://github.com/WeihanLi/WeihanLi.Common/blob/dev/docs/ReleaseNotes.md\n    </PackageReleaseNotes>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Serilog\" />\n    <ProjectReference Include=\"..\\WeihanLi.Common\\WeihanLi.Common.csproj\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "src/WeihanLi.Extensions.Hosting/CronBasedBackgroundService.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\nusing System.Diagnostics;\nusing System.Diagnostics.Metrics;\nusing WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Helpers.Hosting;\n\nnamespace WeihanLi.Extensions.Hosting;\n\npublic abstract class CronBasedBackgroundService : Microsoft.Extensions.Hosting.BackgroundService, IHostedService\n{\n    protected abstract string CronExpression { get; }\n\n    protected abstract Task ExecuteTaskAsync(CancellationToken stoppingToken);\n\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        var next = CronHelper.GetNextOccurrence(CronExpression);\n        while (!stoppingToken.IsCancellationRequested && next.HasValue)\n        {\n            var now = DateTimeOffset.UtcNow;\n            if (now >= next)\n            {\n                _ = ExecuteTaskAsync(stoppingToken);\n                next = CronHelper.GetNextOccurrence(CronExpression);\n                if (!next.HasValue) break;\n            }\n            else\n            {\n                var delay = next.Value - DateTimeOffset.UtcNow;\n                await Task.Delay(delay, stoppingToken);\n            }\n        }\n    }\n}\n\npublic abstract class CronBasedBackgroundServiceWithDiagnostic : CronBasedBackgroundService\n{\n    private readonly IServiceProvider _serviceProvider;\n    private readonly Counter<int> _executeCounter;\n\n    protected CronBasedBackgroundServiceWithDiagnostic(IServiceProvider serviceProvider)\n    {\n        _serviceProvider = serviceProvider;\n        _executeCounter = DiagnosticHelper.Meter.CreateCounter<int>(\"cron-service-executed-counter\", \"count\", \"CronBasedBackgroundService execute count(status:[0: success, -1: error])\");\n        Logger = serviceProvider.GetRequiredService<ILoggerFactory>()\n            .CreateLogger(GetType());\n    }\n\n    protected ILogger Logger { get; }\n\n    protected abstract Task ExecuteTaskInternalAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken);\n\n    protected override async Task ExecuteTaskAsync(CancellationToken stoppingToken)\n    {\n        using var scope = _serviceProvider.CreateScope();\n        using var activity = DiagnosticHelper.ActivitySource.StartActivity();\n        try\n        {\n            Logger.LogInformation(\"BackgroundService execute begin\");\n            await ExecuteTaskInternalAsync(scope.ServiceProvider, stoppingToken);\n            Logger.LogInformation(\"BackgroundService execute end\");\n            if (_executeCounter.Enabled) _executeCounter.Add(1, new KeyValuePair<string, object?>(\"status\", \"0\"));\n        }\n        catch (Exception e)\n        {\n            activity?.SetStatus(ActivityStatusCode.Error, e.Message);\n            Logger.LogError(e, \"BackgroundService execute exception\");\n            if (_executeCounter.Enabled) _executeCounter.Add(1, new KeyValuePair<string, object?>(\"status\", \"-1\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Extensions.Hosting/HostingExtensions.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\n\nnamespace WeihanLi.Extensions.Hosting;\n\npublic static class HostingExtensions\n{\n    public static IHostApplicationBuilder ConfigureHostOptions(this IHostApplicationBuilder hostApplicationBuilder, Action<HostOptions> optionsConfigure)\n    {\n        ArgumentNullException.ThrowIfNull(hostApplicationBuilder);\n        ArgumentNullException.ThrowIfNull(optionsConfigure);\n\n        hostApplicationBuilder.Services.Configure(optionsConfigure);\n\n        return hostApplicationBuilder;\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Extensions.Hosting/README.md",
    "content": "# WeihanLi.Extensions.Hosting\n\n## Intro\n\nextensions for `Microsoft.Extensions.Hosting` and customized hosting\n\n- `TimerBasedBackgroundService`\n- `CronBasedBackgroundService`\n"
  },
  {
    "path": "src/WeihanLi.Extensions.Hosting/TimerBaseBackgroundService.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing Microsoft.Extensions.Logging;\nusing System.Diagnostics;\nusing System.Diagnostics.Metrics;\nusing WeihanLi.Common.Helpers;\nusing IMyHostedService = WeihanLi.Common.Helpers.Hosting.IHostedService;\n\nnamespace WeihanLi.Extensions.Hosting;\n\npublic abstract class TimerBaseBackgroundService : BackgroundService, IMyHostedService\n{\n    protected abstract TimeSpan Period { get; }\n    protected abstract Task ExecuteTaskAsync(CancellationToken stoppingToken);\n\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        using var timer = new PeriodicTimer(Period);\n        while (await timer.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false))\n        {\n            await ExecuteTaskAsync(stoppingToken).ConfigureAwait(false);\n        }\n    }\n}\n\npublic abstract class TimerBaseBackgroundServiceWithDiagnostic : TimerBaseBackgroundService\n{\n    private readonly IServiceProvider _serviceProvider;\n    private readonly Counter<int> _executeCounter;\n\n    protected TimerBaseBackgroundServiceWithDiagnostic(IServiceProvider serviceProvider)\n    {\n        _serviceProvider = serviceProvider;\n        _executeCounter = DiagnosticHelper.Meter.CreateCounter<int>(\"timer-service-executed-counter\", \"count\", \"TimerBaseBackgroundService execute count(status:[0: success, -1: error])\");\n        Logger = _serviceProvider.GetRequiredService<ILoggerFactory>()\n            .CreateLogger(GetType());\n    }\n\n    protected ILogger Logger { get; }\n\n    protected abstract Task ExecuteTaskInternalAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken);\n\n    protected override async Task ExecuteTaskAsync(CancellationToken stoppingToken)\n    {\n        using var scope = _serviceProvider.CreateScope();\n        using var activity = DiagnosticHelper.ActivitySource.StartActivity();\n        try\n        {\n            Logger.LogInformation(\"BackgroundService execute begin\");\n            await ExecuteTaskInternalAsync(scope.ServiceProvider, stoppingToken);\n            Logger.LogInformation(\"BackgroundService execute end\");\n            if (_executeCounter.Enabled) _executeCounter.Add(1, new KeyValuePair<string, object?>(\"status\", \"0\"));\n        }\n        catch (Exception e)\n        {\n            activity?.SetStatus(ActivityStatusCode.Error, e.Message);\n            Logger.LogError(e, \"BackgroundService execute exception\");\n            if (_executeCounter.Enabled) _executeCounter.Add(1, new KeyValuePair<string, object?>(\"status\", \"-1\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/WeihanLi.Extensions.Hosting/WeihanLi.Extensions.Hosting.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n      <TargetFramework>net8.0</TargetFramework>\n      <Title>WeihanLi.Extensions.Hosting</Title>    \n      <Description>Hosting extensions</Description>\n      <PackageSummary>Hosting extensions</PackageSummary>\n      <PackageProjectUrl>https://github.com/WeihanLi/WeihanLi.Common/tree/dev/src/WeihanLi.Extensions.Hosting</PackageProjectUrl>\n      <PackageTags>$(PackageTags);Hosting</PackageTags>\n      <PackageReleaseNotes>\n        https://github.com/WeihanLi/WeihanLi.Common/releases\n      </PackageReleaseNotes>\n    </PropertyGroup>\n\n    <ItemGroup>\n      <PackageReference Include=\"Microsoft.Extensions.Hosting\" />\n    </ItemGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\WeihanLi.Common\\WeihanLi.Common.csproj\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/AspectTest/ProxyFactoryTest.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Options;\nusing WeihanLi.Common.Aspect;\nusing WeihanLi.Common.Event;\nusing WeihanLi.Common.Services;\nusing WeihanLi.Common.Test.EventsTest;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.AspectTest;\n\npublic class ProxyFactoryTest\n{\n    private const string NamespacePrefix = \"FluentAspects.DynamicGenerated\";\n    private readonly IServiceProvider _serviceProvider;\n    private readonly IProxyFactory _proxyFactory;\n\n    public ProxyFactoryTest()\n    {\n        var services = new ServiceCollection();\n        services.AddFluentAspects();\n        services.AddEvents()\n            ;\n\n        services.AddTransientProxy<TestEvent>();\n        services.AddTransientProxy<IEventHandler<TestEvent>, TestEventHandler>();\n        services.AddTransientProxy<IUserIdProvider, EnvironmentUserIdProvider>();\n\n        _serviceProvider = services.BuildServiceProvider();\n\n        _proxyFactory = _serviceProvider.GetRequiredService<IProxyFactory>();\n    }\n\n    [Fact]\n    public void GetProxyFromDependencyInjection()\n    {\n        var userIdProviderProxy = _serviceProvider.GetRequiredService<IUserIdProvider>();\n        Assert.NotNull(userIdProviderProxy);\n        Assert.True(userIdProviderProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n\n        var userId = userIdProviderProxy.GetUserId();\n        Assert.NotNull(userId);\n\n        var eventProxy = _serviceProvider.GetRequiredService<TestEvent>();\n        Assert.NotNull(eventProxy);\n        Assert.True(eventProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n    }\n\n    [Fact]\n    public void CreateInstanceWithoutArguments()\n    {\n        var userIdProviderProxy = _proxyFactory.CreateProxy<IUserIdProvider>();\n        Assert.NotNull(userIdProviderProxy);\n        Assert.True(userIdProviderProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n\n        var userIdProviderProxy2 = _proxyFactory.CreateProxy<IUserIdProvider, EnvironmentUserIdProvider>();\n        Assert.NotNull(userIdProviderProxy2);\n        Assert.True(userIdProviderProxy2.GetType().Namespace?.StartsWith(NamespacePrefix));\n\n        var userId = userIdProviderProxy2.GetUserId();\n        Assert.NotNull(userId);\n\n        var eventProxy = _proxyFactory.CreateProxy<TestEvent>();\n        Assert.NotNull(eventProxy);\n        Assert.True(eventProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n    }\n\n    [Fact]\n    public async Task CreateInstanceWithArguments()\n    {\n        var eventPublisherProxy = _proxyFactory.CreateProxy<IEventPublisher, EventQueuePublisher>();\n        Assert.NotNull(eventPublisherProxy);\n        Assert.True(eventPublisherProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n        await eventPublisherProxy.PublishAsync(new TestEvent());\n\n        eventPublisherProxy = _proxyFactory.CreateProxy<EventQueuePublisher>();\n        Assert.NotNull(eventPublisherProxy);\n        Assert.True(eventPublisherProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n        await eventPublisherProxy.PublishAsync(new TestEvent());\n\n        var options = new OptionsWrapper<EventQueuePublisherOptions>(new EventQueuePublisherOptions());\n        eventPublisherProxy = _proxyFactory.CreateProxy<EventQueuePublisher>(options);\n        Assert.NotNull(eventPublisherProxy);\n        Assert.True(eventPublisherProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n        await eventPublisherProxy.PublishAsync(new TestEvent());\n    }\n\n    [Fact]\n    public void CreateInstanceOfInterfaceWithInherit()\n    {\n        var eventBusProxy = _proxyFactory.CreateProxy<IEventBus>();\n        Assert.NotNull(eventBusProxy);\n        Assert.True(eventBusProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n\n        // eventBusProxy.PublishAsync(new TestEvent()).Wait();\n    }\n\n    [Fact]\n    public void CreateProxyInstanceOfAbstract()\n    {\n        var userIdProviderProxy = _proxyFactory.CreateProxy<IUserIdProvider>();\n        Assert.NotNull(userIdProviderProxy);\n        Assert.True(userIdProviderProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n        userIdProviderProxy.GetUserId();\n\n        var eventHandlerProxy = _proxyFactory.CreateProxy<EventHandlerBase<TestEvent>>();\n        Assert.NotNull(eventHandlerProxy);\n        Assert.True(eventHandlerProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n        eventHandlerProxy.Handle(new TestEvent(), new());\n    }\n\n    [Fact]\n    public void CommonProxyMethodInvokeTest()\n    {\n        var userIdProviderProxy = _proxyFactory.CreateProxy<IUserIdProvider, EnvironmentUserIdProvider>();\n        var userId = userIdProviderProxy.GetUserId();\n        Assert.NotNull(userId);\n        Assert.NotEmpty(userId!);\n    }\n\n    [Fact]\n    public async Task GenericProxyMethodInvokeTest()\n    {\n        var eventQueue = _serviceProvider.GetRequiredService<IEventQueue>();\n        var queueCount = (await eventQueue.GetQueuesAsync()).Count;\n        Assert.Equal(0, queueCount);\n\n        var eventPublisherProxy = _proxyFactory.CreateProxy<IEventPublisher, EventQueuePublisher>();\n        await eventPublisherProxy.PublishAsync(new TestEvent());\n        queueCount = (await eventQueue.GetQueuesAsync()).Count;\n        Assert.Equal(1, queueCount);\n    }\n\n    [Fact]\n    public async Task EventHandlerTest()\n    {\n        Assert.Equal(0, TestEventHandler.Count);\n        var eventBus = _serviceProvider.GetRequiredService<IEventBus>();\n        await eventBus.PublishAsync(new TestEvent());\n        Assert.Equal(1, TestEventHandler.Count);\n    }\n\n    [Fact]\n    public void GenericTypeTest()\n    {\n        var proxyTypeFactory = _serviceProvider.GetRequiredService<IProxyTypeFactory>();\n        var proxyType = proxyTypeFactory.CreateProxyType(typeof(EventHandlerBase<>));\n        Assert.NotNull(proxyType);\n        Assert.True(proxyType.IsGenericTypeDefinition);\n        Assert.True(proxyType.IsGenericType);\n        Assert.NotNull(proxyType.BaseType);\n\n        var eventHandlerProxy = _proxyFactory.CreateProxy<EventHandlerBase<TestEvent>>();\n        Assert.NotNull(eventHandlerProxy);\n        Assert.True(eventHandlerProxy.GetType().Namespace?.StartsWith(NamespacePrefix));\n        eventHandlerProxy.Handle(new TestEvent(), new());\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/AspectTest/ServiceCollectionBuildTest.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\nusing WeihanLi.Common.Aspect;\nusing WeihanLi.Common.Event;\nusing WeihanLi.Common.Services;\nusing WeihanLi.Common.Test.EventsTest;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.AspectTest;\n\npublic class ServiceCollectionBuildTest\n{\n    public class TestGenericEventHandler<TEvent> : EventHandlerBase<TEvent> where TEvent : class, IEventBase\n    {\n        public override Task Handle(TEvent @event, EventProperties eventProperties) => Task.CompletedTask;\n    }\n\n    private readonly IServiceProvider _serviceProvider;\n\n    public ServiceCollectionBuildTest(ITestOutputHelper output)\n    {\n        var services = new ServiceCollection();\n        services.AddSingleton<IIdGenerator, GuidIdGenerator>();\n        services.AddSingleton(GuidIdGenerator.Instance);\n        services.AddSingleton<IUserIdProvider, EnvironmentUserIdProvider>();\n        services.AddSingleton<EnvironmentUserIdProvider>();\n\n        services.AddSingleton<IEventPublisher, EventQueuePublisher>();\n        services.AddSingleton<IEventQueue, EventQueueInMemory>();\n        services.AddOptions();\n\n        services.AddSingleton<EventHandlerBase<TestEvent>>(new DelegateEventHandler<TestEvent>(_ => { }));\n\n        services.AddSingleton(typeof(IEventHandler<>), typeof(TestGenericEventHandler<>));\n\n        _serviceProvider = services.BuildFluentAspectsProvider(options =>\n        {\n            options.InterceptAll()\n                .With<TestOutputInterceptor>();\n        });\n    }\n\n    [Fact]\n    public void InterfaceTest()\n    {\n        // unsealed implement\n        var userIdProvider = _serviceProvider.GetRequiredService<IUserIdProvider>();\n        Assert.NotNull(userIdProvider);\n\n        var userIdProviderType = userIdProvider.GetType();\n        Assert.True(userIdProviderType.IsSealed);\n        Assert.True(userIdProviderType.Assembly.IsDynamic);\n\n        var userId = userIdProvider.GetUserId();\n        Assert.NotNull(userId);\n\n        // sealed implement\n        var idGenerator = _serviceProvider.GetRequiredService<IIdGenerator>();\n        Assert.NotNull(idGenerator);\n        var idGeneratorType = idGenerator.GetType();\n        Assert.True(idGeneratorType.IsSealed);\n        Assert.True(idGeneratorType.Assembly.IsDynamic);\n\n        var newId = idGenerator.NewId();\n        Assert.NotNull(newId);\n    }\n\n    [Fact]\n    public async Task ClassTest()\n    {\n        // unsealed class, will intercept virtual method\n        var userIdProvider = _serviceProvider.GetRequiredService<EnvironmentUserIdProvider>();\n        Assert.NotNull(userIdProvider);\n\n        var userIdProviderType = userIdProvider.GetType();\n        Assert.True(userIdProviderType.IsSealed);\n        Assert.True(userIdProviderType.Assembly.IsDynamic);\n\n        var userId = userIdProvider.GetUserId();\n        Assert.NotNull(userId);\n\n        // sealed class, will not be intercepted\n        var idGenerator = _serviceProvider.GetRequiredService<GuidIdGenerator>();\n        Assert.NotNull(idGenerator);\n        var idGeneratorType = idGenerator.GetType();\n        Assert.True(idGeneratorType.IsSealed);\n        Assert.False(idGeneratorType.Assembly.IsDynamic);\n\n        var newId = idGenerator.NewId();\n        Assert.NotNull(newId);\n\n        // unsealed service, sealed implement\n        var eventHandler = _serviceProvider.GetRequiredService<EventHandlerBase<TestEvent>>();\n        Assert.NotNull(eventHandler);\n        var eventHandlerType = eventHandler.GetType();\n\n        Assert.True(eventHandlerType.IsSealed);\n        Assert.True(eventHandlerType.Assembly.IsDynamic);\n\n        var handTask = eventHandler.Handle(new TestEvent(), new());\n        Assert.NotNull(handTask);\n        await handTask;\n    }\n\n    [Fact]\n    public async Task GenericMethodTest()\n    {\n        var publisher = _serviceProvider.GetRequiredService<IEventPublisher>();\n        Assert.NotNull(publisher);\n        var publisherType = publisher.GetType();\n        Assert.True(publisherType.IsSealed);\n        Assert.True(publisherType.Assembly.IsDynamic);\n\n        await publisher.PublishAsync(new TestEvent());\n    }\n\n    // not supported, will not intercept\n    [Fact]\n    public void OpenGenericTypeTest()\n    {\n        var eventHandler = _serviceProvider.GetRequiredService<IEventHandler<TestEvent>>();\n        Assert.NotNull(eventHandler);\n        var eventHandlerType = eventHandler.GetType();\n\n        Assert.True(eventHandlerType.IsGenericType);\n        //Assert.False(eventHandlerType.Assembly.IsDynamic);\n\n        //eventHandler.Handle(new TestEvent()).Wait();\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/AspectTest/ServiceContainerBuilderBuildTest.cs",
    "content": "﻿using Microsoft.Extensions.Options;\nusing WeihanLi.Common.Aspect;\nusing WeihanLi.Common.DependencyInjection;\nusing WeihanLi.Common.Event;\nusing WeihanLi.Common.Services;\nusing WeihanLi.Common.Test.EventsTest;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.AspectTest;\n\npublic class ServiceContainerBuilderBuildTest\n{\n    private readonly IServiceProvider _serviceProvider;\n\n    public ServiceContainerBuilderBuildTest(ITestOutputHelper output)\n    {\n        var services = new ServiceContainerBuilder();\n        services.AddSingleton<IIdGenerator, GuidIdGenerator>();\n        services.AddSingleton(GuidIdGenerator.Instance);\n        services.AddSingleton<IUserIdProvider, EnvironmentUserIdProvider>();\n        services.AddSingleton<EnvironmentUserIdProvider>();\n\n        services.AddSingleton<IEventPublisher, EventQueuePublisher>();\n        services.AddSingleton<IOptions<EventQueuePublisherOptions>>(\n            new OptionsWrapper<EventQueuePublisherOptions>(new EventQueuePublisherOptions()));\n        services.AddSingleton<IEventQueue, EventQueueInMemory>();\n\n        services.AddSingleton<EventHandlerBase<TestEvent>>(new DelegateEventHandler<TestEvent>(_ => { }));\n\n        services.AddSingleton(typeof(IEventHandler<>), typeof(ServiceCollectionBuildTest.TestGenericEventHandler<>));\n\n        _serviceProvider = services.BuildFluentAspectsContainer(options =>\n        {\n            options.InterceptAll()\n                .With<TestOutputInterceptor>();\n        });\n    }\n\n    [Fact]\n    public void InterfaceTest()\n    {\n        // unsealed implement\n        var userIdProvider = _serviceProvider.ResolveRequiredService<IUserIdProvider>();\n        Assert.NotNull(userIdProvider);\n\n        var userIdProviderType = userIdProvider.GetType();\n        Assert.True(userIdProviderType.IsSealed);\n        Assert.True(userIdProviderType.Assembly.IsDynamic);\n\n        var userId = userIdProvider.GetUserId();\n        Assert.NotNull(userId);\n\n        // sealed implement\n        var idGenerator = _serviceProvider.ResolveRequiredService<IIdGenerator>();\n        Assert.NotNull(idGenerator);\n        var idGeneratorType = idGenerator.GetType();\n        Assert.True(idGeneratorType.IsSealed);\n        Assert.True(idGeneratorType.Assembly.IsDynamic);\n\n        var newId = idGenerator.NewId();\n        Assert.NotNull(newId);\n    }\n\n    [Fact]\n    public async Task ClassTest()\n    {\n        // unsealed class, will intercept virtual method\n        var userIdProvider = _serviceProvider.ResolveRequiredService<EnvironmentUserIdProvider>();\n        Assert.NotNull(userIdProvider);\n\n        var userIdProviderType = userIdProvider.GetType();\n        Assert.True(userIdProviderType.IsSealed);\n        Assert.True(userIdProviderType.Assembly.IsDynamic);\n\n        var userId = userIdProvider.GetUserId();\n        Assert.NotNull(userId);\n\n        // sealed class, will not be intercepted\n        var idGenerator = _serviceProvider.ResolveRequiredService<GuidIdGenerator>();\n        Assert.NotNull(idGenerator);\n        var idGeneratorType = idGenerator.GetType();\n        Assert.True(idGeneratorType.IsSealed);\n        Assert.False(idGeneratorType.Assembly.IsDynamic);\n\n        var newId = idGenerator.NewId();\n        Assert.NotNull(newId);\n\n        // unsealed service, sealed implement\n        var eventHandler = _serviceProvider.ResolveRequiredService<EventHandlerBase<TestEvent>>();\n        Assert.NotNull(eventHandler);\n        var eventHandlerType = eventHandler.GetType();\n\n        Assert.True(eventHandlerType.IsSealed);\n        Assert.True(eventHandlerType.Assembly.IsDynamic);\n\n        var handTask = eventHandler.Handle(new TestEvent(), new());\n        Assert.NotNull(handTask);\n        await handTask;\n    }\n\n    // not supported, will not intercept\n    [Fact]\n    public void OpenGenericTypeTest()\n    {\n        var eventHandler = _serviceProvider.ResolveRequiredService<IEventHandler<TestEvent>>();\n        Assert.NotNull(eventHandler);\n        var eventHandlerType = eventHandler.GetType();\n\n        Assert.True(eventHandlerType.IsGenericType);\n        //Assert.False(eventHandlerType.Assembly.IsDynamic);\n\n        //eventHandler.Handle(new TestEvent()).Wait();\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/AspectTest/TestOutputInterceptor.cs",
    "content": "﻿using System.Diagnostics;\nusing WeihanLi.Common.Aspect;\nusing WeihanLi.Extensions;\n\nnamespace WeihanLi.Common.Test.AspectTest;\n\npublic class TestOutputInterceptor : IInterceptor\n{\n    public async Task Invoke(IInvocation invocation, Func<Task> next)\n    {\n        Debug.WriteLine($\"Method[{invocation.ProxyMethod.Name}({invocation.Arguments.StringJoin(\",\")})] is invoking...\");\n        await next();\n        Debug.WriteLine($\"Method[{invocation.ProxyMethod.Name}({invocation.Arguments.StringJoin(\",\")})] invoked...\");\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/AsyncLockTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test;\n\npublic class AsyncLockTest\n{\n    [Fact]\n    public void LockTest()\n    {\n        using var locker = new AsyncLock();\n        int num = 0, count = 100;\n        //\n        Parallel.For(0, count, _ =>\n        {\n            // ReSharper disable once AccessToDisposedClosure\n            using (locker.Lock())\n            {\n                num++;\n            }\n        });\n        Assert.Equal(count, num);\n    }\n\n    [Fact]\n    public async Task LockAsyncTest()\n    {\n        using var locker = new AsyncLock();\n        int num = 0, count = 100;\n        //\n        await Enumerable.Range(1, count)\n            .Select(async _ =>\n            {\n                // ReSharper disable once AccessToDisposedClosure\n                using (await locker.LockAsync())\n                {\n                    num++;\n                }\n            })\n            .WhenAll();\n        Assert.Equal(count, num);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/CacheUtilTest.cs",
    "content": "﻿using WeihanLi.Common.Models;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test;\n\npublic class CacheUtilTest\n{\n    [Fact]\n    public void GetTypeProperty()\n    {\n        var properties = CacheUtil.GetTypeProperties(typeof(Category));\n        Assert.True(properties.SequenceEqual(typeof(Category).GetProperties()));\n\n        var obj = new { Name = \"Alice\", Age = 10 };\n        properties = CacheUtil.GetTypeProperties(obj.GetType());\n        Assert.True(properties.SequenceEqual(obj.GetType().GetProperties()));\n    }\n\n    [Fact]\n    public void GetTypeField()\n    {\n        var fields = CacheUtil.GetTypeFields(typeof(Category));\n        Assert.True(fields.SequenceEqual(typeof(Category).GetFields()));\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/CompressorTest/DataCompressorTest.cs",
    "content": "﻿using System.Text;\nusing WeihanLi.Common.Compressor;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.CompressorTest;\n\npublic class DataCompressorTest\n{\n    [Fact]\n    public void NullCompressorTest()\n    {\n        var compressor = new NullDataCompressor();\n        var bytes = Encoding.UTF8.GetBytes(\"Compressor Test\");\n        var compressedBytes = compressor.Compress(bytes);\n        Assert.True(compressedBytes.SequenceEqual(bytes));\n        var decompressedBytes = compressor.Decompress(compressedBytes);\n        Assert.True(decompressedBytes.SequenceEqual(bytes));\n    }\n\n    [Fact]\n    public async Task NullCompressorAsyncTest()\n    {\n        var compressor = new NullDataCompressor();\n        var bytes = Encoding.UTF8.GetBytes(\"Compressor Test\");\n        var compressedBytes = await compressor.CompressAsync(bytes);\n        Assert.True(compressedBytes.SequenceEqual(bytes));\n        var decompressedBytes = await compressor.DecompressAsync(compressedBytes);\n        Assert.True(decompressedBytes.SequenceEqual(bytes));\n    }\n\n    [Theory]\n    //[InlineData(10)]\n    [InlineData(256)]\n    [InlineData(257)]\n    [InlineData(500)]\n    [InlineData(100_000)]\n    public void GZipCompressorTest(int len)\n    {\n        var compressor = new GZipDataCompressor();\n        var str = new string('a', len);\n        var bytes = Encoding.UTF8.GetBytes(str);\n        var compressedBytes = compressor.Compress(bytes);\n        var decompressedBytes = compressor.Decompress(compressedBytes);\n        var text = Encoding.UTF8.GetString(decompressedBytes);\n        Assert.Equal(str, text);\n    }\n\n    [Theory]\n    [InlineData(256)]\n    [InlineData(257)]\n    [InlineData(500)]\n    [InlineData(100_000)]\n    public async Task GZipCompressorAsyncTest(int len)\n    {\n        var compressor = new GZipDataCompressor();\n        var str = new string('a', len);\n        var bytes = Encoding.UTF8.GetBytes(str);\n        var compressedBytes = await compressor.CompressAsync(bytes);\n        var decompressedBytes = await compressor.DecompressAsync(compressedBytes);\n        var text = Encoding.UTF8.GetString(decompressedBytes);\n        Assert.Equal(str, text);\n\n        Assert.Equal(str, compressedBytes.DecompressGZipString());\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/DependencyInjectionTest.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing WeihanLi.Common.DependencyInjection;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test;\n\npublic class DependencyInjectionTest : IDisposable\n{\n    private readonly IServiceContainer _container;\n\n    public DependencyInjectionTest()\n    {\n        var containerBuilder = new ServiceContainerBuilder();\n        containerBuilder.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());\n        containerBuilder.AddScoped<IFly, MonkeyKing>();\n        containerBuilder.AddScoped<IFly, Superman>();\n\n        containerBuilder.AddScoped<HasDependencyTest>();\n        containerBuilder.AddScoped<HasDependencyTest1>();\n        containerBuilder.AddScoped<HasDependencyTest2>();\n        containerBuilder.AddScoped<HasDependencyTest3>();\n        containerBuilder.AddScoped(typeof(HasDependencyTest4<>));\n\n        containerBuilder.AddTransient<WuKong>();\n        containerBuilder.AddScoped<WuJing>(_ => new WuJing());\n        containerBuilder.AddSingleton(typeof(GenericServiceTest<>));\n\n        _container = containerBuilder.Build();\n    }\n\n    [Fact]\n    public void Test()\n    {\n        var rootConfig = _container.ResolveService<IConfiguration>();\n\n        Assert.Throws<InvalidOperationException>(() => _container.ResolveService<IFly>());\n        Assert.Throws<InvalidOperationException>(() => _container.ResolveRequiredService<IDependencyResolver>());\n\n        using (var scope = _container.CreateScope())\n        {\n            var config = scope.ResolveService<IConfiguration>();\n\n            Assert.Equal(rootConfig, config);\n\n            var fly1 = scope.ResolveRequiredService<IFly>();\n            if (fly1 is Superman superman)\n            {\n                Assert.Null(superman.Configuration);\n                Assert.NotNull(superman.Configuration1);\n            }\n\n            var fly2 = scope.ResolveRequiredService<IFly>();\n            Assert.Equal(fly1, fly2);\n\n            var wukong1 = scope.ResolveRequiredService<WuKong>();\n            var wukong2 = scope.ResolveRequiredService<WuKong>();\n\n            Assert.NotEqual(wukong1, wukong2);\n\n            var wuJing1 = scope.ResolveRequiredService<WuJing>();\n            var wuJing2 = scope.ResolveRequiredService<WuJing>();\n\n            Assert.Equal(wuJing1, wuJing2);\n\n            var s0 = scope.ResolveRequiredService<HasDependencyTest>();\n            s0.Test();\n            Assert.Equal(s0._fly, fly1);\n\n            var s1 = scope.ResolveRequiredService<HasDependencyTest1>();\n            s1.Test();\n\n            var s2 = scope.ResolveRequiredService<HasDependencyTest2>();\n            s2.Test();\n\n            var s3 = scope.ResolveRequiredService<HasDependencyTest3>();\n            s3.Test();\n\n            var s4 = scope.ResolveRequiredService<HasDependencyTest4<string>>();\n            s4.Test();\n\n            using (var innerScope = scope.CreateScope())\n            {\n                var config2 = innerScope.ResolveRequiredService<IConfiguration>();\n                Assert.True(rootConfig == config2);\n\n                var fly3 = innerScope.ResolveRequiredService<IFly>();\n                fly3.Fly();\n\n                Assert.NotEqual(fly1, fly3);\n            }\n\n            var flySvcs = scope.ResolveServices<IFly>();\n            foreach (var f in flySvcs)\n                f.Fly();\n        }\n\n        var genericService1 = _container.ResolveRequiredService<GenericServiceTest<int>>();\n        genericService1.Test();\n\n        var genericService2 = _container.ResolveRequiredService<GenericServiceTest<string>>();\n        genericService2.Test();\n    }\n\n    public void Dispose()\n    {\n        _container.Dispose();\n    }\n\n    #region Private services\n\n    private class WuKong : IDisposable\n    {\n        public WuKong()\n        {\n            Console.WriteLine(\"wukong initialized\");\n        }\n\n        public void Jump()\n        {\n            Console.WriteLine(\"wukong jumped 一万八千里\");\n        }\n\n        public void Dispose()\n        {\n            Console.WriteLine(\"wukong disposed\");\n        }\n    }\n\n    private class WuJing : IDisposable\n    {\n        public WuJing()\n        {\n            Console.WriteLine(\"WuJing initialized\");\n        }\n\n        public void Eat()\n        {\n            Console.WriteLine(\"WuJing eated balaba.....\");\n        }\n\n        public void Dispose()\n        {\n            Console.WriteLine(\"WuJing disposed\");\n        }\n    }\n\n    private class GenericServiceTest<T>\n    {\n        public void Test()\n        {\n            Console.WriteLine($\"generic type: {typeof(T).FullName}\");\n        }\n    }\n\n    private class HasDependencyTest\n    {\n        public readonly IFly _fly;\n\n        public HasDependencyTest(IFly fly)\n        {\n            _fly = fly;\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {nameof(HasDependencyTest)}\");\n            _fly.Fly();\n        }\n    }\n\n    private class HasDependencyTest1\n    {\n        private readonly IReadOnlyCollection<IFly> _flys;\n\n        public HasDependencyTest1(IEnumerable<IFly> flys)\n        {\n            _flys = flys.ToArray();\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {nameof(HasDependencyTest1)}\");\n            foreach (var item in _flys)\n            {\n                item.Fly();\n            }\n        }\n    }\n\n    private class HasDependencyTest2\n    {\n        private readonly IReadOnlyCollection<IFly> _flys;\n\n        public HasDependencyTest2(IReadOnlyCollection<IFly> flys)\n        {\n            _flys = flys;\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {nameof(HasDependencyTest2)}\");\n            foreach (var item in _flys)\n            {\n                item.Fly();\n            }\n        }\n    }\n\n    private class HasDependencyTest3\n    {\n        private readonly IReadOnlyCollection<GenericServiceTest<int>> _svcs;\n\n        public HasDependencyTest3(IEnumerable<GenericServiceTest<int>> svcs)\n        {\n            _svcs = svcs.ToArray();\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {nameof(HasDependencyTest3)}\");\n            foreach (var item in _svcs)\n            {\n                item.Test();\n            }\n        }\n    }\n\n    private class HasDependencyTest4<T>\n    {\n        private readonly IReadOnlyCollection<GenericServiceTest<T>> _svcs;\n\n        public HasDependencyTest4(IEnumerable<GenericServiceTest<T>> svcs)\n        {\n            _svcs = svcs.ToArray();\n        }\n\n        public void Test()\n        {\n            Console.WriteLine($\"test in {GetType().FullName}\");\n            foreach (var item in _svcs)\n            {\n                item.Test();\n            }\n        }\n    }\n\n    private interface IFly\n    {\n        string Name { get; }\n\n        void Fly();\n    }\n\n    private class MonkeyKing : IFly, IDisposable\n    {\n        public string Name => \"MonkeyKing\";\n\n        public void Fly()\n        {\n            Console.WriteLine($\"{Name} is flying\");\n        }\n\n        public void Dispose()\n        {\n            Console.WriteLine($\"{Name}  disposed...\");\n        }\n    }\n\n    private class Superman : IFly\n    {\n        public IConfiguration? Configuration { get; set; }\n\n        [FromService]\n        public IConfiguration? Configuration1 { get; set; }\n\n        public string Name => \"Superman\";\n\n        public void Fly()\n        {\n            Console.WriteLine(\"Superman is flying\");\n        }\n    }\n\n    #endregion Private services\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/DisposalTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test;\n\npublic class DisposalTest\n{\n    [Fact]\n    public void DisposableActionTest()\n    {\n        var a = 0;\n        var disposal = new DisposableAction(() => a++);\n        disposal.Dispose();\n        Assert.Equal(1, a);\n    }\n\n    [Fact]\n    public void DisposableActionParallelTest()\n    {\n        var a = 0;\n        var disposal = new DisposableAction(() => a++);\n        Parallel.For(1, 10, _ =>\n        {\n            disposal.Dispose();\n        });\n        Assert.Equal(1, a);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/EventsTest/AckQueueTest.cs",
    "content": "﻿using WeihanLi.Common.Event;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.EventsTest;\n\npublic class AckQueueTest\n{\n    private readonly AckQueue _ackQueue = new(new()\n    {\n        AutoRequeue = false\n    });\n\n    [Fact]\n    public async Task EnqueueAsync_ShouldAddMessageToQueue()\n    {\n        var testEvent = new TestEvent { Message = \"Test Message\" };\n        await _ackQueue.EnqueueAsync(testEvent);\n\n        var dequeuedEvent = await _ackQueue.DequeueAsync<TestEvent>();\n        Assert.NotNull(dequeuedEvent);\n        Assert.Equal(testEvent.Message, dequeuedEvent.Data.Message);\n    }\n\n    [Fact]\n    public async Task DequeueAsync_ShouldRetrieveMessageWithoutRemoval()\n    {\n        var testEvent = new TestEvent { Message = \"Test Message\" };\n        await _ackQueue.EnqueueAsync(testEvent);\n\n        var dequeuedEvent1 = await _ackQueue.DequeueAsync<TestEvent>();\n        var dequeuedEvent2 = await _ackQueue.DequeueAsync<TestEvent>();\n\n        Assert.NotNull(dequeuedEvent1);\n        Assert.Equal(testEvent.Message, dequeuedEvent1.Data.Message);\n        Assert.Null(dequeuedEvent2);\n    }\n\n    [Fact]\n    public async Task AckMessageAsync_ShouldAcknowledgeAndRemoveMessage()\n    {\n        var testEvent = new TestEvent { Message = \"Test Message\" };\n        await _ackQueue.EnqueueAsync(testEvent);\n\n        var dequeuedEvent = await _ackQueue.DequeueAsync<TestEvent>();\n        Assert.NotNull(dequeuedEvent);\n\n        await _ackQueue.AckMessageAsync(dequeuedEvent.Properties.EventId);\n\n        var dequeuedEventAfterAck = await _ackQueue.DequeueAsync<TestEvent>();\n        Assert.Null(dequeuedEventAfterAck);\n    }\n\n    [Fact]\n    public async Task RequeueUnAckedMessagesAsync_ShouldRequeueUnAckedMessagesAfterTimeout()\n    {\n        var testEvent = new TestEvent { Message = \"Test Message\" };\n        var ackQueue = new AckQueue(new()\n        {\n            AutoRequeue = false,\n            AckTimeout = TimeSpan.FromSeconds(3)\n        });\n        await ackQueue.EnqueueAsync(testEvent);\n\n        var dequeuedEvent = await ackQueue.DequeueAsync<TestEvent>();\n        Assert.NotNull(dequeuedEvent);\n\n        // Simulate timeout\n        await Task.Delay(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);\n\n        ackQueue.RequeueUnAckedMessages();\n\n        var requeuedEvent = await ackQueue.DequeueAsync<TestEvent>();\n        Assert.NotNull(requeuedEvent);\n        Assert.Equal(testEvent.Message, requeuedEvent.Data.Message);\n    }\n\n    [Fact]\n    public async Task AutoRequeueUnAckedMessagesAsync_ShouldRequeueUnAckedMessagesAfterTimeout()\n    {\n        var testEvent = new TestEvent { Message = \"Test Message\" };\n        await using var ackQueue = new AckQueue(new()\n        {\n            AutoRequeue = true,\n            AckTimeout = TimeSpan.FromSeconds(3),\n            RequeuePeriod = TimeSpan.FromSeconds(2)\n        });\n        await ackQueue.EnqueueAsync(testEvent);\n\n        var dequeuedEvent = await ackQueue.DequeueAsync<TestEvent>();\n        Assert.NotNull(dequeuedEvent);\n\n        // Simulate timeout\n        await Task.Delay(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);\n\n        var requeuedEvent = await ackQueue.DequeueAsync<TestEvent>();\n        Assert.NotNull(requeuedEvent);\n        Assert.Equal(testEvent.Message, requeuedEvent.Data.Message);\n    }\n\n    private class TestEvent\n    {\n        public string Message { get; set; } = string.Empty;\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/EventsTest/EventBaseTest.cs",
    "content": "﻿using WeihanLi.Common.Event;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.EventsTest;\n\npublic class TestEvent : EventBase\n{\n    public string Name { get; set; } = null!;\n}\n\npublic class TestEventHandler : EventHandlerBase<TestEvent>\n{\n    public static int Count;\n\n    public override Task Handle(TestEvent @event, EventProperties eventProperties)\n    {\n        Count++;\n        return Task.CompletedTask;\n    }\n}\n\npublic class EventBaseTest\n{\n    [Fact]\n    public void EventDeserializeTest()\n    {\n        var testEvent = new TestEvent()\n        {\n            Name = \"1213\"\n        };\n        var eventDataJson = testEvent.ToJson();\n        var deserializedEvent = eventDataJson.JsonToObject<TestEvent>();\n        Assert.Equal(testEvent.EventId, deserializedEvent.EventId);\n        Assert.Equal(testEvent.EventAt, deserializedEvent.EventAt);\n        Assert.Equal(testEvent.Name, deserializedEvent.Name);\n    }\n\n    [Fact]\n    public void EventMessageExtensionsTest()\n    {\n        var testEvent = new TestEvent()\n        {\n            Name = \"1213\"\n        };\n        var eventMsg = testEvent.ToEventRawMsg();\n        var eventFromMsg = eventMsg.ToEvent<TestEvent>();\n        Assert.Equal(typeof(TestEvent), eventFromMsg.GetType());\n\n        var deserializedEvent = eventFromMsg as TestEvent;\n        Assert.NotNull(deserializedEvent);\n        Assert.Equal(testEvent.EventId, deserializedEvent.EventId);\n        Assert.Equal(testEvent.EventAt, deserializedEvent.EventAt);\n        Assert.Equal(testEvent.Name, deserializedEvent.Name);\n    }\n\n    [Fact]\n    public void EventMessageExtensions2Test()\n    {\n        var testEvent = new TestEvent()\n        {\n            Name = \"1213\"\n        };\n        var eventMsg = testEvent.ToEventMsg();\n        var eventFromMsg = eventMsg.ToEvent<EventWrapper<TestEvent>>();\n        Assert.Equal(typeof(EventWrapper<TestEvent>), eventFromMsg.GetType());\n\n        var deserializedEvent = eventFromMsg.Data;\n        Assert.NotNull(deserializedEvent);\n        Assert.Equal(testEvent.EventId, deserializedEvent.EventId);\n        Assert.Equal(testEvent.EventAt, deserializedEvent.EventAt);\n        Assert.Equal(testEvent.Name, deserializedEvent.Name);\n        Assert.Equal(testEvent.EventId, eventFromMsg.Properties.EventId);\n        Assert.Equal(testEvent.EventAt, eventFromMsg.Properties.EventAt);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/EventsTest/EventBusTest.cs",
    "content": "﻿using Microsoft.Extensions.DependencyInjection;\nusing WeihanLi.Common.Event;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.EventsTest;\n\npublic class EventBusTest\n{\n    private static int _counter, _counter1;\n    private readonly IServiceProvider _serviceProvider;\n\n    public EventBusTest()\n    {\n        IServiceCollection serviceCollection = new ServiceCollection();\n        serviceCollection.AddEvents()\n            .AddEventHandler<TestEvent, TestEventHandler1>()\n            .AddEventHandler<TestEvent, TestEventHandler2>()\n            .AddEventHandler<TestEvent, TestEventHandler3<TestEvent>>()\n            .AddEventHandler<TestEvent1, TestEventHandler3<TestEvent1>>()\n            ;\n        _serviceProvider = serviceCollection.BuildServiceProvider();\n    }\n\n    [Fact]\n    public async Task MainTest()\n    {\n        var eventBus = _serviceProvider.GetRequiredService<IEventBus>();\n        await eventBus.PublishAsync(new TestEvent());\n        Assert.Equal(3, _counter);\n        await eventBus.PublishAsync(new TestEvent1());\n        Assert.Equal(1, _counter1);\n        await eventBus.PublishAsync(new TestEvent1());\n        Assert.Equal(2, _counter1);\n    }\n\n    public class TestEvent : EventBase\n    {\n        public string? Name { get; set; }\n    }\n\n    public class TestEvent1 : EventBase\n    {\n        public string? Name { get; set; }\n    }\n\n    public class TestEventHandler1 : EventHandlerBase<TestEvent>\n    {\n        public override Task Handle(TestEvent @event, EventProperties eventProperties)\n        {\n            Interlocked.Increment(ref _counter);\n            return Task.CompletedTask;\n        }\n    }\n\n    public class TestEventHandler2 : EventHandlerBase<TestEvent>\n    {\n        public override Task Handle(TestEvent @event, EventProperties eventProperties)\n        {\n            Interlocked.Increment(ref _counter);\n            return Task.CompletedTask;\n        }\n    }\n\n    public class TestEventHandler3<TEvent> : EventHandlerBase<TEvent>\n        where TEvent : class, IEventBase\n    {\n        public override Task Handle(TEvent @event, EventProperties eventProperties)\n        {\n            if (@event.GetType() == typeof(TestEvent))\n            {\n                Interlocked.Increment(ref _counter);\n            }\n            else\n            {\n                Interlocked.Increment(ref _counter1);\n            }\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ExtensionsTest/CollectionExtensionTest.cs",
    "content": "﻿using System.Collections.Specialized;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ExtensionsTest;\n\npublic class CollectionExtensionTest\n{\n    [Fact]\n    public void NameValueCollectionToDictionaryTest()\n    {\n        var collection = new NameValueCollection()\n            {\n                {\"Test1\", \"Test1\"},\n                {\"Test2\", \"Test1\"},\n                {\"Test3\", \"Test1\"},\n                {\"Test4\", \"Test1\"},\n            };\n        var dic = collection.ToDictionary();\n        Assert.Equal(collection.Count, dic.Count);\n        foreach (var key in collection.AllKeys)\n        {\n            Assert.True(dic.ContainsKey(key!));\n            Assert.Equal(collection.Get(key), dic[key!]);\n        }\n    }\n\n    [Fact]\n    public void NameValueCollectionToQueryStringTest()\n    {\n        var collection = new NameValueCollection();\n        Assert.Equal(string.Empty, collection.ToQueryString());\n        Assert.Equal(string.Empty, ((NameValueCollection)null!).ToQueryString());\n\n        collection = new NameValueCollection()\n            {\n                {\"Test1\", \"Test1\"},\n                {\"Test2\", \"Test1\"},\n                {\"Test3\", \"Test1\"}\n            };\n        Assert.Equal(\"Test1=Test1&Test2=Test1&Test3=Test1\", collection.ToQueryString());\n    }\n\n    [Fact]\n    public void ListRemoveWhereTest()\n    {\n        var list = Enumerable.Range(1, 10).ToList();\n        list.RemoveWhere(x => x > 6);\n        Assert.False(list.Exists(x => x > 6));\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ExtensionsTest/CompressExtensionTest.cs",
    "content": "﻿using System.Text;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ExtensionsTest;\n\npublic class CompressExtensionTest\n{\n    [Fact]\n    public void CompressStringTest()\n    {\n        var str = new string('1', 10000);\n        var compressBytes = str.CompressGZip();\n        Assert.Equal(str, Encoding.UTF8.GetString(compressBytes.DecompressGZip()));\n        var compressStr = str.GetBytes().CompressGZipString();\n        Assert.Equal(compressStr, Encoding.UTF8.GetString(compressBytes));\n        Assert.Equal(str, Encoding.UTF8.GetString(compressBytes.DecompressGZip()));\n        Assert.Equal(str, compressBytes.DecompressGZipString());\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ExtensionsTest/ConfigurationExtensionTest.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ExtensionsTest;\n\npublic class ConfigurationExtensionTest\n{\n    [Theory]\n    [InlineData(\"true\")]\n    [InlineData(\"false\")]\n    [InlineData(\"false-value\")]\n    [InlineData(null)]\n    public void FeatureFlagTest(string? value)\n    {\n        var featureName = \"TestFeature\";\n        var configuration = new ConfigurationBuilder()\n            .AddInMemoryCollection(new[]\n            {\n                    new KeyValuePair<string, string?>($\"{ConfigurationExtension.FeatureFlagsSectionName}:{featureName}\", value)\n            })\n            .Build();\n\n        var parseResult = bool.TryParse(value, out var flagValue);\n        Assert.Equal(parseResult, configuration.TryGetFeatureFlagValue(featureName, out _));\n        Assert.Equal(flagValue, configuration.IsFeatureEnabled(featureName));\n    }\n\n    [Fact]\n    public void FeatureFlag_ConfigrationSectionNotExists()\n    {\n        var featureName = \"TestFeature\";\n        var configuration = new ConfigurationBuilder()\n            .AddInMemoryCollection()\n            .Build();\n        Assert.False(configuration.TryGetFeatureFlagValue(featureName, out _));\n\n        Assert.False(configuration.IsFeatureEnabled(featureName));\n        Assert.True(configuration.IsFeatureEnabled(featureName, true));\n    }\n\n    [Fact]\n    public void FeatureFlag_FeatureNameNotExists()\n    {\n        var featureName = \"TestFeature\";\n        var configuration = new ConfigurationBuilder()\n                 .AddInMemoryCollection(new[]\n                 {\n                         new KeyValuePair<string, string?>($\"{ConfigurationExtension.FeatureFlagsSectionName}:Test\",\"\")\n                 })\n                 .Build();\n        Assert.False(configuration.TryGetFeatureFlagValue(featureName, out _));\n\n        Assert.False(configuration.IsFeatureEnabled(featureName));\n        Assert.True(configuration.IsFeatureEnabled(featureName, true));\n    }\n\n    [Fact]\n    public void FeatureFlag_FeatureFlagValueIsNull()\n    {\n        var featureName = \"TestFeature\";\n        var configuration = new ConfigurationBuilder()\n                 .AddInMemoryCollection(new[]\n                 {\n                         new KeyValuePair<string, string?>($\"{ConfigurationExtension.FeatureFlagsSectionName}:{featureName}\", null)\n                 })\n                 .Build();\n        Assert.False(configuration.TryGetFeatureFlagValue(featureName, out _));\n\n        Assert.False(configuration.IsFeatureEnabled(featureName));\n        Assert.True(configuration.IsFeatureEnabled(featureName, true));\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ExtensionsTest/CoreExtensionTest.cs",
    "content": "﻿using WeihanLi.Common.Models;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ExtensionsTest;\n\n/// <summary>\n/// CoreExtensionTest\n/// </summary>\npublic class CoreExtensionTest\n{\n    #region ObjectExtension\n\n    [Fact]\n    public void ToGenericTest()\n    {\n        var num1 = 1.2;\n        var toNum1 = num1.To<decimal>();\n        Assert.Equal(typeof(decimal), toNum1.GetType());\n        Assert.Equal(typeof(double), toNum1.To<double>().GetType());\n\n        // nullable test\n        var nullableNum = num1.To<decimal?>();\n        Assert.NotNull(nullableNum);\n        Assert.Equal(1.2m, nullableNum!.Value);\n        Assert.Equal(1.2m, nullableNum.To<decimal>());\n        // nullable to nullable\n        var nullableNum2 = nullableNum.To<double?>();\n        Assert.NotNull(nullableNum2);\n        Assert.Equal(1.2d, nullableNum2!.Value);\n\n        // int to bool test\n        Assert.False(0.To<bool>());\n        Assert.True(1.To<bool>());\n    }\n\n    [Fact]\n    public void ToTest()\n    {\n        var num1 = 1.2;\n        var toNum1 = num1.To(typeof(decimal))!;\n        Assert.Equal(typeof(decimal), toNum1.GetType());\n        Assert.Equal(typeof(double), toNum1.To(typeof(double))!.GetType());\n\n        // nullable test\n        var nullableNum = num1.To(typeof(decimal?));\n        Assert.NotNull(nullableNum);\n        Assert.Equal(1.2m, ((decimal?)nullableNum).GetValueOrDefault());\n        Assert.Equal(1.2m, nullableNum.To(typeof(decimal)));\n        // nullable to nullable\n        var nullableNum2 = nullableNum.To(typeof(double?));\n        Assert.NotNull(nullableNum2);\n        Assert.Equal(1.2d, (double)nullableNum2.To(typeof(double))!);\n\n        // int to bool test\n        Assert.False((bool)0.To(typeof(bool))!);\n        Assert.True((bool)1.To(typeof(bool))!);\n    }\n\n    #endregion ObjectExtension\n\n    #region BoolExtensions\n\n    [Fact]\n    public void IfTest()\n    {\n        var num = 1;\n        true.IfTrue(() => num++);\n        Assert.Equal(2, num);\n        false.IfTrue(() => num++);\n        Assert.Equal(2, num);\n        true.IfFalse(() => num++);\n        Assert.Equal(2, num);\n        false.IfFalse(() => num++);\n        Assert.Equal(3, num);\n    }\n\n    #endregion BoolExtensions\n\n    #region StringExtensionTest\n\n    [Fact]\n    public void SafeSubstring()\n    {\n        var str = \"abcdefg\";\n        Assert.Equal(str.Substring(1, 2), str.SafeSubstring(1, 2));\n        Assert.Equal(\"bcdefg\", str.SafeSubstring(1, 20));\n        Assert.Equal(\"\", str.SafeSubstring(10, 20));\n\n        Assert.Equal(str[str.Length..], str.SafeSubstring(str.Length));\n        Assert.Equal(\"\", str.SafeSubstring(10));\n    }\n\n    [Fact]\n    public void Sub()\n    {\n        var str = \"abcdef\";\n        Assert.Equal(\"ef\", str.Sub(-2));\n        Assert.Equal(\"def\", str.Sub(3));\n    }\n\n    [Theory]\n    [InlineData(\"ASD0123\")]\n    [InlineData(\"Hello World\")]\n    public void HexStringConvert(string input)\n    {\n        var inputBytes = input.GetBytes();\n        var hexString = Convert.ToHexString(inputBytes);\n        var bytes = hexString.HexStringToBytes();\n        Assert.NotNull(bytes);\n        Assert.NotEmpty(bytes);\n        Assert.True(inputBytes.SequenceEqual(bytes));\n        Assert.Equal(hexString, bytes.ToHexString());\n    }\n\n    #endregion StringExtensionTest\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ExtensionsTest/EnumerableExtensionTest.cs",
    "content": "﻿using WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ExtensionsTest;\n\npublic class EnumerableExtensionTest\n{\n    [Fact]\n    public void SplitTest()\n    {\n        var data = Enumerable.Range(1, 20);\n        var array = data.Split(6).ToArray();\n        Assert.Equal(4, array.Length);\n        Assert.All(array, Assert.NotNull);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ExtensionsTest/ServiceCollectionExtensionTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.DependencyInjection;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ExtensionsTest;\n\npublic class ServiceCollectionExtensionTest\n{\n    [Fact]\n    public void DecorateNonGenericType()\n    {\n        var services = new ServiceCollection();\n        services.AddSingleton<IJob, Sleepy>();\n        services.Decorate<IJob, JobDecorator>();\n        using var sp = services.BuildServiceProvider();\n\n        var job = sp.GetRequiredService<IJob>();\n        Assert.IsType<JobDecorator>(job);\n        Assert.Equal($\"??? {nameof(Sleepy)}\", job.Name);\n    }\n\n    //[Fact]\n    //public void DecorateOpenGenericType()\n    //{\n    //    var services = new ServiceCollection();\n    //    services.AddSingleton(typeof(IValueProvider<>), typeof(DefaultValueProvider<>));\n    //    services.Decorate(typeof(IValueProvider<>), typeof(ValueProviderDecorator<>));\n    //    using var sp = services.BuildServiceProvider();\n\n    //    var service = sp.GetRequiredService<IValueProvider<int>>();\n    //    Assert.IsType<ValueProviderDecorator<int>>(service);\n    //    service.GetValue();\n    //}\n\n    private interface IJob\n    {\n        string Name { get; }\n        void Execute();\n    }\n\n    private sealed class Sleepy : IJob\n    {\n        public string Name => nameof(Sleepy);\n\n        public void Execute()\n        {\n            Console.WriteLine(\"Sleeping...\");\n        }\n    }\n\n    private sealed class JobDecorator(IJob job) : IJob\n    {\n        private readonly IJob _job = job;\n\n        public string Name => $\"??? {_job.Name}\";\n\n        public void Execute()\n        {\n            Console.WriteLine(\"Before execute\");\n\n            _job.Execute();\n\n            Console.WriteLine(\"After execute\");\n        }\n    }\n    private interface IValueProvider<T>\n    {\n        T? GetValue();\n    }\n    private class DefaultValueProvider<T> : IValueProvider<T>\n    {\n        public T? GetValue()\n        {\n            return default;\n        }\n    }\n    private sealed class ValueProviderDecorator<T>(IValueProvider<T> valueProvider) : IValueProvider<T>\n    {\n        private readonly IValueProvider<T> _valueProvider = valueProvider;\n\n        public int Counter { get; private set; }\n\n        public T? GetValue()\n        {\n            var value = _valueProvider.GetValue();\n            Counter++;\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ExtensionsTest/StringExtensionTest.cs",
    "content": "﻿using WeihanLi.Common.Models;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ExtensionsTest;\n\npublic class StringExtensionTest\n{\n    [Theory]\n    [InlineData(typeof(uint), \"uint\")]\n    [InlineData(typeof(int), \"int\")]\n    [InlineData(typeof(bool), \"bool\")]\n    [InlineData(typeof(byte), \"byte\")]\n    [InlineData(typeof(short), \"short\")]\n    [InlineData(typeof(float), \"float\")]\n    [InlineData(typeof(double), \"double\")]\n    [InlineData(typeof(long), \"long\")]\n    [InlineData(typeof(decimal), \"decimal\")]\n    [InlineData(typeof(Guid), \"guid\")]\n    [InlineData(typeof(DateTime), \"datetime\")]\n    [InlineData(typeof(string), \"string\")]\n    [InlineData(typeof(string), \"String\")]\n    [InlineData(typeof(string), \"System.String\")]\n    [InlineData(typeof(Category), \"WeihanLi.Common.Models.Category\")]\n    public void GetTypeByTypeName(Type type, string name)\n    {\n        Assert.Equal(type, name.GetTypeByTypeName());\n    }\n\n    [Theory]\n    [InlineData(\"axx\", \"123\")]\n    [InlineData(\"\", \"123\")]\n    [InlineData(\" \", \"123\")]\n    [InlineData(null, \"123\")]\n    public void GetNotEmptyValue(string? value, string defaultValue)\n    {\n        var expected = string.IsNullOrEmpty(value) ? defaultValue : value;\n        Assert.Equal(expected, value.GetNotEmptyValueOrDefault(defaultValue));\n    }\n\n    [Theory]\n    [InlineData(\"axx\", \"123\")]\n    [InlineData(\"\", \"123\")]\n    [InlineData(\" \", \"123\")]\n    [InlineData(null, \"123\")]\n    public void StringGetValue(string? value, string defaultValue)\n    {\n        var expected = string.IsNullOrWhiteSpace(value) ? defaultValue : value;\n        Assert.Equal(expected, value.GetValueOrDefault(defaultValue));\n    }\n\n    [Theory]\n    [InlineData(\"axx\", \"123\")]\n    [InlineData(\"axx\", \"\")]\n    [InlineData(\"12345\", \"123\")]\n    [InlineData(\"\", \"123\")]\n    [InlineData(null, \"123\")]\n    [InlineData(null, null)]\n    public void TrimStart(string? value, string? start)\n    {\n        var expected = start.IsNotNullOrEmpty() && value?.StartsWith(start!) == true ? value[start!.Length..] : value;\n        Assert.Equal(expected, value!.TrimStart(start!));\n    }\n\n    [Theory]\n    [InlineData(\"axx\")]\n    [InlineData(\"12345\")]\n    [InlineData(\" \")]\n    [InlineData(\"\")]\n    [InlineData(null)]\n    public void IsNullOrEmpty(string? value)\n    {\n        Assert.Equal(string.IsNullOrEmpty(value), value.IsNullOrEmpty());\n    }\n\n    [Theory]\n    [InlineData(\"axx\")]\n    [InlineData(\"12345\")]\n    [InlineData(\" \")]\n    [InlineData(\"\")]\n    [InlineData(null)]\n    public void IsNotNullOrEmpty(string? value)\n    {\n        Assert.Equal(!string.IsNullOrEmpty(value), value.IsNotNullOrEmpty());\n    }\n\n    [Theory]\n    [InlineData(\"axx\")]\n    [InlineData(\"12345\")]\n    [InlineData(\" \")]\n    [InlineData(\"\")]\n    [InlineData(null)]\n    public void IsNullOrWhiteSpace(string? value)\n    {\n        Assert.Equal(string.IsNullOrWhiteSpace(value), value.IsNullOrWhiteSpace());\n    }\n\n    [Theory]\n    [InlineData(\"axx\")]\n    [InlineData(\"12345\")]\n    [InlineData(\" \")]\n    [InlineData(\"\")]\n    [InlineData(null)]\n    public void IsNotNullOrWhiteSpace(string? value)\n    {\n        Assert.Equal(!string.IsNullOrWhiteSpace(value), value.IsNotNullOrWhiteSpace());\n    }\n\n    [Fact]\n    public void SplitArray()\n    {\n        var count = 5;\n\n        var str = Enumerable.Range(1, count).StringJoin(\",\");\n        var array = str.SplitArray<int>();\n        Assert.Equal(count, array.Length);\n        Assert.True(array.SequenceEqual(Enumerable.Range(1, count)));\n\n        str = Enumerable.Range(1, count).StringJoin(\";\");\n        array = str.SplitArray<int>([';']);\n        Assert.Equal(count, array.Length);\n        Assert.True(array.SequenceEqual(Enumerable.Range(1, count)));\n\n        var array1 = str.SplitArray<int?>([';']);\n        Assert.Equal(count, array1.Length);\n        Assert.True(array1.Select(x => x.GetValueOrDefault()).SequenceEqual(Enumerable.Range(1, count)));\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ExtensionsTest/TaskExtensionsTest.cs",
    "content": "﻿using WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ExtensionsTest;\n\npublic class TaskExtensionsTest\n{\n    [Fact]\n    public void CancellationTokenAsTask()\n    {\n        using var cts = new CancellationTokenSource();\n        var cancellationToken = cts.Token;\n\n        var task = cancellationToken.AsTask();\n        Assert.False(task.IsCompleted);\n        Assert.False(cancellationToken.IsCancellationRequested);\n\n        cts.Cancel();\n        Assert.True(task.IsCompleted);\n        Assert.True(cancellationToken.IsCancellationRequested);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ExtensionsTest/TypeExtensionTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Test.EventsTest;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ExtensionsTest;\n\npublic class TypeExtensionTest\n{\n    [Fact]\n    public void IsBasicTypeTest()\n    {\n        var types = new[]\n        {\n                typeof(bool),\n\n                typeof(sbyte),\n                typeof(byte),\n                typeof(int),\n                typeof(uint),\n                typeof(short),\n                typeof(ushort),\n                typeof(long),\n                typeof(ulong),\n                typeof(float),\n                typeof(double),\n                typeof(decimal),\n\n                typeof(DateTime),// IsPrimitive:False\n                typeof(TimeSpan),// IsPrimitive:False\n\n                typeof(char),\n                typeof(string),// IsPrimitive:False\n\n                //typeof(object),// IsPrimitive:False\n            };\n        Assert.All(types, t => Assert.True(t.IsBasicType()));\n    }\n\n    [Fact]\n    public void GetDefaultValueTest()\n    {\n        Assert.Equal(default(int), typeof(int).GetDefaultValue());\n        Assert.Equal(default(bool), typeof(bool).GetDefaultValue());\n        Assert.Equal(default(TestEvent), typeof(TestEvent).GetDefaultValue());\n        Assert.Null(typeof(void).GetDefaultValue());\n    }\n\n    [Theory]\n    [InlineData(false, typeof(void))]\n    [InlineData(false, typeof(int))]\n    [InlineData(true, typeof(Task<int>))]\n    [InlineData(true, typeof(ValueTask<int>))]\n    public void IsTypeAwaitableTest(bool result, Type type)\n    {\n        Assert.Equal(result, type.IsAwaitable());\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/ApplicationHelperTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class ApplicationHelperTest\n{\n    [Fact]\n    public void DotnetPathTest()\n    {\n        var dotnetPath = ApplicationHelper.GetDotnetPath();\n        Assert.NotNull(dotnetPath);\n        Assert.NotEmpty(dotnetPath);\n        Assert.True(File.Exists(dotnetPath));\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/BoundedConcurrentQueue.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic sealed class BoundedConcurrentQueueTest\n{\n    [Fact]\n    public void FullQueue_DropWrite()\n    {\n        var queue = new BoundedConcurrentQueue<object?>(1);\n        Assert.True(queue.TryEnqueue(null));\n        Assert.Equal(1, queue.Count);\n        Assert.False(queue.TryEnqueue(null));\n        Assert.Equal(1, queue.Count);\n    }\n\n    [Fact]\n    public void FullQueue_DropOldest()\n    {\n        var queue = new BoundedConcurrentQueue<object?>(1, BoundedQueueFullMode.DropOldest);\n        Assert.True(queue.TryEnqueue(null));\n        Assert.Equal(1, queue.Count);\n        Assert.True(queue.TryEnqueue(null));\n        Assert.Equal(1, queue.Count);\n    }\n\n    [Fact]\n    public void NonBounded()\n    {\n        var queue = new BoundedConcurrentQueue<object?>();\n        Assert.True(queue.TryEnqueue(null));\n        Assert.Equal(1, queue.Count);\n        Assert.True(queue.TryEnqueue(null));\n        Assert.Equal(2, queue.Count);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/CommandExecutorTest.cs",
    "content": "﻿using System.Net;\nusing System.Runtime.InteropServices;\nusing WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class CommandExecutorTest\n{\n    [Fact]\n    public void HostNameTest()\n    {\n        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n        {\n            return;\n        }\n\n        var result = CommandExecutor.ExecuteAndCapture(\"hostname\");\n\n        var hostName = Dns.GetHostName();\n        Assert.Equal(hostName, result.StandardOut.TrimEnd());\n        Assert.Equal(0, result.ExitCode);\n    }\n\n    [Fact]\n    public async Task HostNameTestAsync()\n    {\n        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n        {\n            return;\n        }\n\n        var result = await CommandExecutor.ExecuteAndCaptureAsync(\"hostname\", cancellationToken: TestContext.Current.CancellationToken);\n\n        var hostName = Dns.GetHostName();\n        Assert.Equal(hostName, result.StandardOut.TrimEnd());\n        Assert.Equal(0, result.ExitCode);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/ConsoleHelperTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class ConsoleHelperTest\n{\n    [Fact]\n    public void ErrorWriteWithColorHandlesNullOutput()\n    {\n        using var consoleOutput = ConsoleOutput.Capture();\n\n        ConsoleHelper.ErrorWriteWithColor(null, ConsoleColor.Red);\n\n        var output = consoleOutput.StandardError;\n        Assert.Empty(output);\n    }\n\n    [Fact]\n    public void ErrorWriteLineWithColorHandlesNullOutput()\n    {\n        using var consoleOutput = ConsoleOutput.Capture();\n        ConsoleHelper.ErrorWriteLineWithColor(null, ConsoleColor.Red);\n        var output = consoleOutput.StandardError;\n        Assert.Equal(Environment.NewLine, output);\n    }\n\n    [Fact]\n    public void SupportsAnsiColorsReturnsSameValueOnMultipleCalls()\n    {\n        var firstCall = ConsoleHelper.SupportsAnsiColors();\n        var secondCall = ConsoleHelper.SupportsAnsiColors();\n        Assert.Equal(firstCall, secondCall);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/EncoderTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class EncoderTest\n{\n    [Fact]\n    public void Base62EncodeTest()\n    {\n        Base62Encoder.Encode(Guid.NewGuid());\n        Base62Encoder.Encode(DateTime.UtcNow.Ticks);\n        Base62Encoder.Encode(\"xxxxxxxxx\");\n        //Base62Encoder.Encode(\"你好\", Encoding.UTF8); // not supported\n    }\n\n    [Fact]\n    public void Base62GuidTest()\n    {\n        var guid = Guid.NewGuid();\n        var encodedText = Base62Encoder.Encode(guid);\n        var decodeGuid = Base62Encoder.DecodeGuid(encodedText);\n        Assert.Equal(guid, decodeGuid);\n    }\n\n    [Theory]\n    [InlineData(12345)]\n    [InlineData(-12345)]\n    [InlineData(123)]\n    [InlineData(-123)]\n    public void Base62LongTest(long num)\n    {\n        var encodedText = Base62Encoder.Encode(num);\n        var decodeNum = Base62Encoder.DecodeLong(encodedText);\n        Assert.Equal(num, decodeNum);\n    }\n\n    [Theory]\n    [InlineData(\"abc\")]\n    [InlineData(\"Alice\")]\n    public void Base62StringTest(string str)\n    {\n        var encodedText = Base62Encoder.Encode(str);\n        var decodeStr = Base62Encoder.DecodeString(encodedText);\n        Assert.Equal(str, decodeStr);\n    }\n\n    [Fact]\n    public void Base36EncodeTest()\n    {\n        Base36Encoder.Encode(Guid.NewGuid());\n        Base36Encoder.Encode(DateTime.UtcNow.Ticks);\n        Base36Encoder.Encode(\"xxxxxxxxx\");\n        //Base36Encoder.Encode(\"你好\", Encoding.UTF8); // not supported\n    }\n\n    [Fact]\n    public void Base36GuidTest()\n    {\n        var guid = Guid.NewGuid();\n        var encodedText = Base36Encoder.Encode(guid);\n        var decodeGuid = Base36Encoder.DecodeGuid(encodedText);\n        Assert.Equal(guid, decodeGuid);\n    }\n\n    [Theory]\n    [InlineData(12345)]\n    [InlineData(-12345)]\n    [InlineData(123)]\n    [InlineData(-123)]\n    public void Base36LongTest(long num)\n    {\n        var encodedText = Base36Encoder.Encode(num);\n        var decodeNum = Base36Encoder.DecodeLong(encodedText);\n        Assert.Equal(num, decodeNum);\n    }\n\n    [Theory]\n    [InlineData(\"abc\")]\n    [InlineData(\"Alice\")]\n    public void Base36StringTest(string str)\n    {\n        var encodedText = Base36Encoder.Encode(str);\n        var decodeStr = Base36Encoder.DecodeString(encodedText);\n        Assert.Equal(str, decodeStr, ignoreCase: true);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/EnumHelperTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Models;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class EnumHelperTest\n{\n    [Fact]\n    public void IdNameListTest()\n    {\n        var list = EnumHelper.ToIdNameList<ReviewState>();\n        Assert.Equal(list.Count, Enum.GetNames<ReviewState>().Length);\n        foreach (var (id, name) in list)\n        {\n            Assert.True(Enum.TryParse(name, out ReviewState state));\n            Assert.Equal((int)state, id);\n        }\n    }\n\n    [Fact]\n    public void IdNameDescListTest()\n    {\n        var list = EnumHelper.ToIdNameDescList<ReviewState, sbyte>();\n        Assert.Equal(list.Count, Enum.GetNames<ReviewState>().Length);\n        foreach (var (id, name, description) in list)\n        {\n            Assert.True(Enum.TryParse(name, out ReviewState state));\n            Assert.Equal((int)state, id);\n            Assert.NotNull(description);\n        }\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/NetHelperTest.cs",
    "content": "﻿using System.Net;\nusing WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class NetHelperTest\n{\n    [Theory]\n    [InlineData(\"10.0.0.135\", true)]\n    [InlineData(\"192.168.0.185\", true)]\n    [InlineData(\"172.16.0.125\", true)]\n    [InlineData(\"172.105.192.135\", false)]\n    [InlineData(\"23.100.91.85\", false)]\n    public void PrivateIPTest(string ip, bool isPrivate)\n    {\n        Assert.Equal(isPrivate, NetHelper.IsPrivateIP(ip));\n    }\n\n    [Theory]\n    [InlineData(\"192.168.0.0/16\", \"192.168.0.185\", true)]\n    [InlineData(\"192.168.0.0/16\", \"172.16.0.125\", false)]\n    [InlineData(\"23.100.91.85\", \"23.100.91.85\", true)]\n    public void IPNetworkTest(string cidr, string ip, bool contains)\n    {\n        Assert.Equal(contains, new WeihanLi.Common.Helpers.IPNetwork(cidr).Contains(IPAddress.Parse(ip)));\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/ProcessExecutorTest.cs",
    "content": "﻿using System.Diagnostics;\nusing System.Net;\nusing System.Runtime.InteropServices;\nusing WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class ProcessExecutorTest\n{\n    [Fact]\n    public async Task HostNameTest()\n    {\n        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n        {\n            return;\n        }\n        using var executor = new ProcessExecutor(\"hostName\");\n        var list = new List<string>();\n        executor.OnOutputDataReceived += (_, str) =>\n        {\n            list.Add(str);\n        };\n        var exitCode = -1;\n        executor.OnExited += (_, code) =>\n        {\n            exitCode = code;\n        };\n        await executor.ExecuteAsync();\n        Assert.NotEmpty(list);\n\n        var hostName = Dns.GetHostName();\n        Assert.Contains(list, x => hostName.Equals(x));\n        Assert.Equal(0, exitCode);\n    }\n\n    // [Fact]\n    // public async Task EnvironmentVariablesTest()\n    // {\n    //     if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n    //     {\n    //         return;\n    //     }\n    //     using var executor = new ProcessExecutor(new ProcessStartInfo(\"powershell\", \"-Command \\\"Write-Host $env:TestUser\\\"\")\n    //     {\n    //         Environment =\n    //             {\n    //                 { \"TestUser\", \"Alice\" }\n    //             }\n    //     });\n    //     var list = new List<string>();\n    //     executor.OnOutputDataReceived += (_, str) =>\n    //     {\n    //         list.Add(str);\n    //     };\n    //     var exitCode = -1;\n    //     executor.OnExited += (_, code) =>\n    //     {\n    //         exitCode = code;\n    //     };\n    //     await executor.ExecuteAsync();\n    //     Assert.NotEmpty(list);\n\n    //     Assert.Contains(list, x => \"Alice\".Equals(x));\n    //     Assert.Equal(0, exitCode);\n    // }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/SecurityHelperTest.cs",
    "content": "﻿using System.Security.Cryptography;\nusing System.Text;\nusing WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class SecurityHelperTest\n{\n    #region GenerateRandomCode\n\n    [Theory]\n    [InlineData(6)]\n    public void GenerateRandomCodeLengthTest(int length)\n    {\n        Assert.Equal(SecurityHelper.GenerateRandomCode(length).Length, length);\n    }\n\n    [Fact]\n    public void GenerateRandomCodeContentTest()\n    {\n        Assert.Matches(\"^([0-9]*)$\", SecurityHelper.GenerateRandomCode(4, true));\n        Assert.Matches(\"^([0-9A-Z]*)$\", SecurityHelper.GenerateRandomCode(4));\n    }\n\n    #endregion GenerateRandomCode\n\n    #region Hash\n\n    [Theory]\n    [InlineData(\"12345\")]\n    public void HashStringTest(string str)\n    {\n        // MD5\n        // Hash result test\n        Assert.Equal(HashHelper.GetHashedString(HashType.MD5, str), SecurityHelper.MD5(str));\n        // case\n        Assert.Equal(HashHelper.GetHashedString(HashType.MD5, str, true), SecurityHelper.MD5(str, true));\n        // Encoding test\n        Assert.Equal(HashHelper.GetHashedString(HashType.MD5, str), HashHelper.GetHashedString(HashType.MD5, str, Encoding.UTF8));\n        // SHA1\n        // Hash result test\n        Assert.Equal(HashHelper.GetHashedString(HashType.SHA1, str), SecurityHelper.SHA1(str));\n        // case\n        Assert.Equal(HashHelper.GetHashedString(HashType.SHA1, str, true), SecurityHelper.SHA1(str, true));\n        // Encoding test\n        Assert.Equal(HashHelper.GetHashedString(HashType.SHA1, str), HashHelper.GetHashedString(HashType.SHA1, str, Encoding.UTF8));\n\n        // Encoding test\n        Assert.Equal(HashHelper.GetHashedString(HashType.SHA256, str), HashHelper.GetHashedString(HashType.SHA256, str, Encoding.UTF8));\n\n        // Encoding test\n        Assert.Equal(HashHelper.GetHashedString(HashType.SHA512, str), HashHelper.GetHashedString(HashType.SHA512, str, Encoding.UTF8));\n    }\n\n    [Fact]\n    public void HashNullOrEmptyTest()\n    {\n        Assert.Equal(\"\", SecurityHelper.MD5(\"\"));\n        Assert.Equal(SecurityHelper.MD5(\"\"), HashHelper.GetHashedString(HashType.MD5, \"\"));\n    }\n\n    #endregion Hash\n\n    [Theory]\n    [InlineData(\"Hello World\")]\n    [InlineData(\"Amazing .NET\")]\n    public void AesEncrypt(string input)\n    {\n        var key = \"1234567890ABCDEF\";\n\n        var encrypted = SecurityHelper.AesEncrypt(input, key);\n        Assert.NotNull(encrypted);\n        Assert.NotEmpty(encrypted);\n\n        var decrypted = SecurityHelper.AesDecrypt(encrypted, key);\n        Assert.Equal(input, decrypted);\n    }\n\n    [Theory]\n    [InlineData(\"Hello World\")]\n    [InlineData(\"Amazing .NET\")]\n    public void AesEncryptWithIV(string input)\n    {\n        var key = \"1234567890ABCDEF\";\n        var iv = SecurityHelper.GenerateRandomCode(16, true);\n\n        var encrypted = SecurityHelper.AesEncrypt(input, key, iv);\n        Assert.NotNull(encrypted);\n        Assert.NotEmpty(encrypted);\n\n        var decrypted = SecurityHelper.AesDecrypt(encrypted, key, iv);\n        Assert.Equal(input, decrypted);\n    }\n\n    [Theory]\n    [InlineData(\"Test1234\", \"287E494548B152837EC742DDE76F0A4F\")]\n    public void AesEncryptExpect(string input, string expectedEncrypted)\n    {\n        var key = \"1234567890ABCDEF\";\n\n        var encrypted = SecurityHelper.AesEncrypt(input, key);\n        Assert.NotNull(encrypted);\n        Assert.NotEmpty(encrypted);\n        Assert.Equal(expectedEncrypted, encrypted);\n\n        var decrypted = SecurityHelper.AesDecrypt(encrypted, key);\n        Assert.Equal(input, decrypted);\n    }\n\n    [Theory]\n    [InlineData(\"Test1234\", \"119CBC45E61827B448D704070EDA0A81\")]\n    public void AesEncryptWithIVExpect(string input, string expectedEncrypted)\n    {\n        var key = \"1234567890ABCDEF\";\n\n        var encrypted = SecurityHelper.AesEncrypt(input, key, key);\n        Assert.NotNull(encrypted);\n        Assert.NotEmpty(encrypted);\n        Assert.Equal(expectedEncrypted, encrypted);\n\n        var decrypted = SecurityHelper.AesDecrypt(encrypted, key, key);\n        Assert.Equal(input, decrypted);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/StringHelperTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class StringHelperTest\n{\n    [Fact]\n    public void HideSensitiveInfo()\n    {\n        var testString = \"12345678901\";\n\n        Assert.Equal(\"132****5489\", StringHelper.HideTelDetails(\"13212345489\"));\n        Assert.Equal(\"123***901\", StringHelper.HideSensitiveInfo(testString, 3, 3, sensitiveCharCount: 3));\n        Assert.Equal(\"1****\", StringHelper.HideSensitiveInfo(testString, 11, 1));\n        Assert.Equal(\"***1\", StringHelper.HideSensitiveInfo(testString, 11, 1, 3, false));\n    }\n\n    [Theory]\n    [InlineData(' ')]\n    // the characters below are not separator\n    //[InlineData(';')]\n    //[InlineData('_')]\n    //[InlineData('-')]\n    public void IsSeparator(char c)\n    {\n        Assert.True(char.IsSeparator(c));\n    }\n\n    [Theory]\n    [InlineData(\"test\", \"Test\")]\n    [InlineData(\"testProject\", \"TestProject\")]\n    [InlineData(\"userName\", \"UserName\")]\n    [InlineData(\"UserName\", \"UserName\")]\n    public void PascalCaseTest(string str1, string str2)\n    {\n        Assert.Equal(str2, StringHelper.ToPascalCase(str1));\n    }\n\n    [Theory]\n    [InlineData(\"test\", \"Test\")]\n    [InlineData(\"testProject\", \"TestProject\")]\n    [InlineData(\"userName\", \"userName\")]\n    [InlineData(\"userName\", \"UserName\")]\n    //[InlineData(\"user name\", \"user name\")]\n    //[InlineData(\"user Name\", \"user Name\")]\n    public void ToCamelCaseTest(string str1, string str2)\n    {\n        Assert.Equal(str1, StringHelper.ToCamelCase(str2));\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/TotpHelperTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class TotpHelperTest\n{\n    [Fact]\n    public void SaltTest()\n    {\n        var bizToken = \"test_xxx\";\n        TotpHelper.ConfigureTotpOptions(options => options.Salt = null);\n        var code = TotpHelper.GetCode(bizToken);\n        Assert.NotEmpty(code);\n\n        TotpHelper.ConfigureTotpOptions(options =>\n        {\n            options.Salt = \"amazing-dotnet\";\n            options.ExpiresIn = 300;\n        });\n        Assert.False(TotpHelper.VerifyCode(bizToken, code));\n\n        var code1 = TotpHelper.GetCode(bizToken);\n        Assert.NotEmpty(code1);\n        Thread.Sleep(2000);\n        Assert.True(TotpHelper.VerifyCode(bizToken, code1));\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HelpersTest/ValidateHelperTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HelpersTest;\n\npublic class ValidateHelperTest\n{\n    [Fact]\n    public void IsEmailTest()\n    {\n        Assert.False(ValidateHelper.IsEmail(null));\n        Assert.False(ValidateHelper.IsEmail(\"\"));\n        Assert.False(ValidateHelper.IsEmail(\"123abc\"));\n        Assert.False(ValidateHelper.IsEmail(\"abc@\"));\n        Assert.True(ValidateHelper.IsEmail(\"abc@outlook.com\"));\n    }\n\n    [Fact]\n    public void IsMobileTest()\n    {\n        Assert.False(ValidateHelper.IsMobile(null));\n        Assert.False(ValidateHelper.IsMobile(\"\"));\n        Assert.False(ValidateHelper.IsMobile(\"123abc\"));\n        Assert.False(ValidateHelper.IsMobile(\"abcdefghijk\"));\n        Assert.True(ValidateHelper.IsMobile(\"13245678901\"));\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/HttpTest/MockHttpHandlerTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the MIT license.\n\nusing System.Net;\nusing WeihanLi.Common.Http;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.HttpTest;\n\npublic class MockHttpHandlerTest\n{\n    [Theory]\n    [InlineData(HttpStatusCode.OK)]\n    [InlineData(HttpStatusCode.BadRequest)]\n    [InlineData(HttpStatusCode.Unauthorized)]\n    [InlineData(HttpStatusCode.Forbidden)]\n    [InlineData(HttpStatusCode.NotFound)]\n    [InlineData(HttpStatusCode.InternalServerError)]\n    public async Task HttpStatusTest(HttpStatusCode httpStatusCode)\n    {\n        using var httpHandler = new MockHttpHandler(_ => new HttpResponseMessage(httpStatusCode));\n        using var httpClient = new HttpClient(httpHandler);\n        using var response = await httpClient.GetAsync(\"http://localhost:32123/api/values\", TestContext.Current.CancellationToken);\n        Assert.Equal(httpStatusCode, response.StatusCode);\n    }\n\n    [Fact]\n    public async Task SetResponseFactoryTest()\n    {\n        using var httpHandler = new MockHttpHandler();\n        using var httpClient = new HttpClient(httpHandler);\n        using var response = await httpClient.GetAsync(\"http://localhost:32123/api/values\", TestContext.Current.CancellationToken);\n        Assert.Equal(HttpStatusCode.OK, response.StatusCode);\n\n        httpHandler.SetResponseFactory(_ => new HttpResponseMessage(HttpStatusCode.BadRequest));\n        using var response1 = await httpClient.GetAsync(\"http://localhost:32123/api/values\", TestContext.Current.CancellationToken);\n        Assert.Equal(HttpStatusCode.BadRequest, response1.StatusCode);\n    }\n\n    [Fact]\n    public async Task DynamicResponseTest()\n    {\n        using var httpHandler = new MockHttpHandler(req => new HttpResponseMessage(HttpStatusCode.OK)\n        {\n            Content = new StringContent(req.Method.Method)\n        });\n        using var httpClient = new HttpClient(httpHandler);\n        var response = await httpClient.GetStringAsync(\"http://localhost:32123/api/values\", TestContext.Current.CancellationToken);\n        Assert.Equal(HttpMethod.Get.Method, response);\n\n        using var httpResponse = await httpClient.PostAsync(\"http://localhost:32123/api/values\", new StringContent(\"\"), TestContext.Current.CancellationToken);\n        response = await httpResponse.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);\n        Assert.Equal(HttpMethod.Post.Method, response);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/IdGeneratorTest.cs",
    "content": "﻿using WeihanLi.Common.Helpers;\nusing WeihanLi.Common.Services;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test;\n\npublic class IdGeneratorTest\n{\n    [Fact]\n    public void GuidIdTest()\n    {\n        var id = GuidIdGenerator.Instance.NewId();\n        Assert.NotEqual(id, GuidIdGenerator.Instance.NewId());\n    }\n\n    [Fact]\n    public void SequentialGuidIdTest()\n    {\n        var idGenerator = new SequentialGuidIdGenerator(SequentialGuidType.SequentialAsString);\n        var id = idGenerator.NewId();\n        Assert.NotEqual(id, idGenerator.NewId());\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/JsonTest/JsonConvertTest.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System.Net;\nusing WeihanLi.Common.Json;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.JsonTest;\n\npublic class JsonConvertTest\n{\n    [Fact]\n    public void JsonTest()\n    {\n        var obj = new\n        {\n            Name = \"Mike\",\n            Age = 10,\n            Class = new { Grade = 7, }\n        };\n        Assert.Equal(JsonConvert.SerializeObject(obj), obj.ToJson());\n    }\n\n    [Fact]\n    public void IpAddressJsonSerializeTest()\n    {\n        var ip = IPAddress.Parse(\"192.168.0.102\");\n        var endPoint = new IPEndPoint(ip, 6379);\n        var str = JsonConvert.SerializeObject(new TestModel\n        {\n            Ip = ip,\n            EndPoint = endPoint\n        }, new IPAddressConverter(), new IPEndPointConverter());\n        var result = JsonConvert.DeserializeObject<TestModel>(str, new IPAddressConverter(), new IPEndPointConverter())!;\n        Assert.NotNull(result);\n        Assert.Equal(ip, result.Ip);\n        Assert.Equal(endPoint, result.EndPoint);\n    }\n\n    private class TestModel\n    {\n        public IPAddress? Ip { get; set; }\n\n        public IPEndPoint? EndPoint { get; set; }\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ModelsTest/PagedListDataTest.cs",
    "content": "﻿using WeihanLi.Common.Models;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ModelsTest;\n\npublic class PagedListModelTest\n{\n    [Fact]\n    public void PagedModelToJsonTest()\n    {\n        var model = new PagedListResult<int>()\n        {\n            PageNumber = 1,\n            PageSize = 3,\n            TotalCount = 10,\n            Data = new[] { 1, 2, 3 }\n        };\n        var json = model.ToJson();\n        var dModel = json.JsonToObject<PagedListResult<int>>();\n        Assert.Equal(model.PageSize, dModel.PageSize);\n    }\n\n    [Fact]\n    public void EmptyPagedListResultTest()\n    {\n        var empty = PagedListResult<int>.Empty;\n        Assert.Empty(empty.Data);\n        Assert.Equal(0, empty.TotalCount);\n        Assert.Equal(1, empty.PageNumber);\n        Assert.Equal(10, empty.PageSize);\n        Assert.Equal(0, empty.PageCount);\n    }\n\n    [Fact]\n    public void ListDataWithTotalToJsonTest()\n    {\n        var model = new ListResultWithTotal<int>()\n        {\n            TotalCount = 10,\n            Data = new[] { 1, 2, 3 }\n        };\n        var json = model.ToJson();\n        var dModel = json.JsonToObject<ListResultWithTotal<int>>();\n        Assert.Equal(model.TotalCount, dModel.TotalCount);\n        Assert.True(dModel.Data.SequenceEqual(model.Data));\n    }\n\n    [Fact]\n    public void ListDataWithTotalEmptyTest()\n    {\n        var empty = ListResultWithTotal<int>.Empty;\n        Assert.NotNull(empty.Data);\n        Assert.Empty(empty.Data);\n        Assert.Equal(0, empty.TotalCount);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ModelsTest/PagedRequestTest.cs",
    "content": "﻿using WeihanLi.Common.Models;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ModelsTest;\n\npublic class PagedRequestTest\n{\n    [Theory]\n    [InlineData(1, 1)]\n    [InlineData(1, 10)]\n    [InlineData(100, 10)]\n    public void ValidPageRequestTest(int pageNum, int pageSize)\n    {\n        var pagedRequest = new PagedRequest()\n        {\n            PageNum = pageNum,\n            PageSize = pageSize,\n        };\n        Assert.Equal(pageNum, pagedRequest.PageNum);\n        Assert.Equal(pageSize, pagedRequest.PageSize);\n    }\n\n    [Theory]\n    [InlineData(-1, -1)]\n    [InlineData(-1, -10)]\n    [InlineData(0, -1)]\n    public void InvalidPageRequestTest(int pageNum, int pageSize)\n    {\n        var pagedRequest = new PagedRequest()\n        {\n            PageNum = pageNum,\n            PageSize = pageSize,\n        };\n        Assert.Equal(1, pagedRequest.PageNum);\n        Assert.Equal(10, pagedRequest.PageSize);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ModelsTest/ResultTest.cs",
    "content": "﻿using WeihanLi.Common.Models;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ModelsTest;\n\npublic class ResultTest\n{\n    [Fact]\n    public void SuccessTest()\n    {\n        var result = Result.Success();\n        Assert.Null(result.Msg);\n        Assert.Equal(ResultStatus.Success, result.Status);\n    }\n\n    [Theory]\n    [InlineData(ResultStatus.Unauthorized)]\n    [InlineData(ResultStatus.Forbidden)]\n    [InlineData(ResultStatus.BadRequest)]\n    [InlineData(ResultStatus.NotImplemented)]\n    [InlineData(ResultStatus.NotFound)]\n    [InlineData(ResultStatus.RequestTimeout)]\n    public void FailTest(ResultStatus resultStatus)\n    {\n        var result = Result.Fail(\"test error\", resultStatus);\n        Assert.Equal(resultStatus, result.Status);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ModelsTest/ReviewModelTest.cs",
    "content": "﻿using WeihanLi.Common.Models;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ModelsTest;\n\npublic class ReviewModelTest\n{\n    [Fact]\n    public void ReviewRequestTest()\n    {\n        var request = new ReviewRequest()\n        {\n            State = ReviewState.Reviewed\n        };\n        Assert.True(request.IsValid());\n\n        request = new ReviewRequest()\n        {\n            State = ReviewState.UnReviewed,\n        };\n        Assert.True(request.IsValid());\n\n        request = new ReviewRequest()\n        {\n            State = ReviewState.Rejected,\n        };\n        Assert.False(request.IsValid());\n\n        request = new ReviewRequest()\n        {\n            State = ReviewState.Rejected,\n            Remark = \"\"\n        };\n        Assert.False(request.IsValid());\n\n        request = new ReviewRequest()\n        {\n            State = ReviewState.Rejected,\n            Remark = \" \"\n        };\n        Assert.False(request.IsValid());\n\n        request = new ReviewRequest()\n        {\n            State = ReviewState.Rejected,\n            Remark = \"just for test\"\n        };\n        Assert.True(request.IsValid());\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ModelsTest/StringValueDictionaryTest.cs",
    "content": "﻿using Newtonsoft.Json;\nusing WeihanLi.Common.Models;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ModelsTest;\n\npublic class StringValueDictionaryTest\n{\n    [Fact]\n    public void EqualsTest()\n    {\n        var abc = new { Id = 1, Name = \"Tom\" };\n        var dic1 = StringValueDictionary.FromObject(abc);\n        var dic2 = StringValueDictionary.FromObject(new Dictionary<string, object>()\n            {\n                {\"Name\", \"Tom\" },\n                {\"Id\", 1},\n            });\n\n        Assert.True(dic1 == dic2);\n        Assert.Equal(dic1, dic2);\n    }\n\n    [Fact]\n    public void DistinctTest()\n    {\n        var abc = new { Id = 1, Name = \"Tom\" };\n        var dic1 = StringValueDictionary.FromObject(abc);\n        var dic2 = StringValueDictionary.FromObject(new Dictionary<string, object>()\n            {\n                { \"Id\", 1 },\n                { \"Name\", \"Tom\" },\n            });\n        var set = new HashSet<StringValueDictionary>\n        {\n            dic1,\n            dic2\n        };\n\n        Assert.Single(set);\n    }\n\n    [Fact]\n    public void CloneTest()\n    {\n        var dic1 = StringValueDictionary.FromObject(new Dictionary<string, object>()\n            {\n                {\"Id\", 1},\n                {\"Name\", \"Tom\" }\n            });\n        var dic2 = dic1.Clone();\n        Assert.False(ReferenceEquals(dic1, dic2));\n        Assert.True(dic1 == dic2);\n    }\n\n    record Person(int Id, string Name);\n\n    [Fact]\n    public void RecordTest()\n    {\n        var str1 = \"{\\\"Id\\\":1, \\\"Name\\\":\\\"Tom\\\"}\";\n        var p1 = JsonConvert.DeserializeObject<Person>(str1);\n\n        var str2 = \"{\\\"Name\\\":\\\"Tom\\\",\\\"Id\\\":1}\";\n        var p2 = JsonConvert.DeserializeObject<Person>(str2);\n\n        Assert.True(p1 == p2);\n        Assert.Equal(p1, p2);\n    }\n\n    [Fact]\n    public void ImplicitConvertTest()\n    {\n        var abc = new { Id = 1, Name = \"Tom\" };\n        var stringValueDictionary = StringValueDictionary.FromObject(abc);\n        Dictionary<string, string> dictionary = stringValueDictionary!;\n        Assert.Equal(stringValueDictionary.Count, dictionary.Count);\n\n        var dic2 = StringValueDictionary.FromObject(dictionary);\n\n        Assert.Equal(dic2, stringValueDictionary);\n        Assert.True(dic2 == stringValueDictionary);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ProfilerTest.cs",
    "content": "﻿using WeihanLi.Common.Services;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test;\n\npublic class ProfilerTest\n{\n    [Theory]\n    [InlineData(500)]\n    [InlineData(1000)]\n    [InlineData(2000)]\n    public void StopWatchProfileTest(int delay)\n    {\n        var profiler = new StopwatchProfiler();\n        profiler.Start();\n        Thread.Sleep(delay * 2);\n        profiler.Stop();\n        Assert.True(profiler.Elapsed.TotalMilliseconds >= delay);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ServicesTest/CancellationTokenProviderTest.cs",
    "content": "﻿using WeihanLi.Common.Services;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ServicesTest;\n\npublic class CancellationTokenProviderTest\n{\n    [Fact]\n    public void NullCancellationTokenProviderTest()\n    {\n        var provider = new NullCancellationTokenProvider();\n        var cancellationToken = provider.GetCancellationToken();\n        Assert.Equal(CancellationToken.None, cancellationToken);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ServicesTest/TotpServiceTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Otp;\nusing WeihanLi.Common.Services;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ServicesTest;\n\npublic class TotpServiceTest\n{\n    [Fact]\n    public void BasicTest()\n    {\n        const string bizToken = \"Test1234\";\n        var totpService = new TotpService(new TotpOptions()\n        {\n            ExpiresIn = 30\n        });\n        var code = totpService.GetCode(bizToken);\n        Assert.NotEmpty(code);\n        Assert.True(totpService.VerifyCode(bizToken, code));\n        Thread.Sleep(35 * 1000);\n        Assert.False(totpService.VerifyCode(bizToken, code));\n    }\n\n    [Fact]\n    public void GetCodeWithTtlTest()\n    {\n        const string bizToken = \"Test1234\";\n        var totpService = new TotpService(new TotpOptions());\n        var (Code, Ttl) = totpService.GetCodeWithTtl(bizToken);\n        Assert.NotEmpty(Code);\n        Assert.True(Ttl >= 1);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/ServicesTest/UserIdProviderTest.cs",
    "content": "﻿using WeihanLi.Common.Services;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.ServicesTest;\n\npublic class UserIdProviderTest\n{\n    [Fact]\n    public void EnvironmentUserIdProviderTest()\n    {\n        IUserIdProvider userIdProvider = EnvironmentUserIdProvider.Instance;\n        var userId = userIdProvider.GetUserId();\n        Assert.Equal(Environment.UserName, userId);\n        Assert.True(userIdProvider.TryGetUserId<string>(out var _));\n        Assert.False(userIdProvider.TryGetUserId<int>(out var _));\n        Assert.Equal(0, userIdProvider.GetUserId<int>());\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/TemplateTest/TemplateParserTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing WeihanLi.Common.Template;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.TemplateTest;\n\npublic class TemplateParserTest\n{\n    [Fact]\n    public async Task PipeParseTest()\n    {\n        var text = \"Hello {{Name | upper}}\";\n        var parser = new DefaultTemplateParser();\n        var context = await parser.ParseAsync(text);\n        Assert.Single(context.Inputs);\n        Assert.Single(context.Inputs.Keys.First().Pipes);\n        Assert.Single(context.Inputs.Keys.First().Pipes);\n        var input = context.Inputs.Keys.First().Input;\n        var pipe = context.Inputs.Keys.First().Pipes.First();\n        Assert.Equal(\"{{Name | upper}}\", input);\n        Assert.Equal(\"upper\", pipe.PipeName);\n        Assert.NotNull(pipe.Arguments);\n        Assert.Empty(pipe.Arguments);\n    }\n\n    [Fact]\n    public async Task ParseTest()\n    {\n        var template = \"\"\"\n                Build Status: {{Status}}\n                Chart: {{$env CHART_NAME}} - {{$env VERSION}}\n                AppVersion: {{$env APP_VERSION}}\n                HostEnv: {{$env HOST | toUpper }}\n                Config: {{$config AppSettings:Host}}\n                Config: {{$config AppSettings--Host}}\n                Config: {{$config AppSettings__Host}}\n                \"\"\";\n        var result = await new DefaultTemplateParser()\n            .ParseAsync(template);\n        var inputs = result.Inputs.Select(x => x.Key.Input)\n            .ToHashSet();\n        Assert.Equal(7, result.Inputs.Count);\n        Assert.Contains(\"{{Status}}\", inputs);\n        Assert.Contains(\"{{$env CHART_NAME}}\", inputs);\n        Assert.Contains(\"{{$env VERSION}}\", inputs);\n        Assert.Contains(\"{{$env APP_VERSION}}\", inputs);\n        Assert.Contains(\"{{$env HOST | toUpper }}\", inputs);\n        Assert.Contains(\"{{$config AppSettings:Host}}\", inputs);\n        Assert.Contains(\"{{$config AppSettings__Host}}\", inputs);\n\n        var variableNames = result.Inputs.Select(x => x.Key.VariableName).ToHashSet();\n        Assert.Contains(\"Status\", variableNames);\n        Assert.Contains(\"CHART_NAME\", variableNames);\n        Assert.Contains(\"VERSION\", variableNames);\n        Assert.Contains(\"APP_VERSION\", variableNames);\n        Assert.Contains(\"AppSettings:Host\", variableNames);\n        Assert.Contains(\"AppSettings__Host\", variableNames);\n        Assert.Contains(\"HOST\", variableNames);\n\n        var pipes = result.Inputs.SelectMany(x => x.Key.Pipes)\n            .Select(x => x.PipeName)\n            .ToArray();\n        Assert.Contains(\"toUpper\", pipes);\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/TemplateTest/TemplateRendererTest.cs",
    "content": "﻿// Copyright (c) Weihan Li. All rights reserved.\n// Licensed under the Apache license.\n\nusing Microsoft.Extensions.Configuration;\nusing WeihanLi.Common.Template;\nusing WeihanLi.Extensions;\nusing Xunit;\n\nnamespace WeihanLi.Common.Test.TemplateTest;\n\npublic class TemplateRendererTest\n{\n    private readonly TemplateEngine _templateEngine = TemplateEngine.CreateDefault(builder =>\n    {\n        builder.ConfigureOptions(options =>\n        {\n            options.Configuration = new ConfigurationBuilder()\n                .AddInMemoryCollection(\n                [\n                    new KeyValuePair<string, string?>(\"Name\", \"test\")\n                ])\n                .Build();\n        });\n        builder.UseTemplatePipe(new SubstringTemplatePipe());\n    });\n\n    [Fact]\n    public async Task VariableRenderTest()\n    {\n        var name = \"mike\";\n        var text = \"Hello {{ Name | toTitle }}\";\n        var renderedText = await _templateEngine.RenderAsync(text, new { Name = name });\n        Assert.Equal($\"Hello {name.ToTitleCase()}\", renderedText);\n    }\n\n    [Fact]\n    public async Task ConfigRenderTest()\n    {\n        var text = \"Hello {{ $config Name | toTitle }}\";\n        var renderedText = await _templateEngine.RenderAsync(text, new { Name = \"mike\" });\n        Assert.Equal($\"Hello {\"test\".ToTitleCase()}\", renderedText);\n    }\n\n    [Fact]\n    public async Task EnvRenderTest()\n    {\n        var text = \"Hello {{$env hostname}}\";\n        var renderedText = await _templateEngine.RenderAsync(text);\n        Assert.Equal($\"Hello {Environment.GetEnvironmentVariable(\"hostname\")}\", renderedText);\n    }\n\n    [Fact]\n    public async Task CustomPipeRenderTest()\n    {\n        var name = \"mike\";\n        var text = \"Hello {{ Name | substr:2 | toTitle }}\";\n        var renderedText = await _templateEngine.RenderAsync(text, new { Name = name });\n        Assert.Equal($\"Hello {name[2..].ToTitleCase()}\", renderedText);\n    }\n}\n\nfile sealed class SubstringTemplatePipe : TemplatePipeBase\n{\n    protected override int? ParameterCount => null;\n    public override string Name => \"substr\";\n    protected override string? ConvertInternal(object? value, params ReadOnlySpan<string> args)\n    {\n        if (args.Length is not (1 or 2))\n        {\n            throw new InvalidOperationException(\"Arguments count must be 1 or 2\");\n        }\n\n        var str = value as string ?? value?.ToString() ?? string.Empty;\n        var start = int.Parse(args[0]);\n        if (args.Length is 1)\n        {\n            return str[start..];\n        }\n        var len = int.Parse(args[1]);\n        return str[start..len];\n    }\n}\n"
  },
  {
    "path": "test/WeihanLi.Common.Test/WeihanLi.Common.Test.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>$(LatestTargetFramework)</TargetFramework>\n    <IsTestProject>true</IsTestProject>\n    <IsPackable>false</IsPackable>\n    <OutputType>exe</OutputType>\n    <UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>\n    <TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Moq\" />\n    <PackageReference Include=\"xunit.v3.mtp-v2\" />\n    <PackageReference Include=\"GitHubActionsTestLogger\" />\n    <PackageReference Include=\"Microsoft.Testing.Platform\" />\n    <PackageReference Include=\"coverlet.collector\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\WeihanLi.Common\\WeihanLi.Common.csproj\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "toc.yml",
    "content": "- name: Home\n  href: index.md\n- name: API Documentation\n  href: docs/api/\n- name: Release Notes\n  href: docs/ReleaseNotes.md\n- name: Articles\n  href: docs/articles/\n  homepage: docs/articles/intro.md\n"
  }
]