From 185a4674e168d056035b4ed978b88c875578a9a6 Mon Sep 17 00:00:00 2001 From: alvaro13bq Date: Sun, 15 Feb 2026 19:42:22 +0100 Subject: [PATCH 1/5] AlvaroFernandez-BackendTask --- .../Controllers/ExchangeRateController.cs | 31 ++++ .../ExchangeRateUpdater.Api.csproj | 23 +++ .../Logs/log-20260215.txt | 7 + .../Middleware/ExceptionHandlingMiddleware.cs | 70 ++++++++ .../ExchangeRateUpdater.API/Program.cs | 55 ++++++ .../Properties/launchSettings.json | 37 ++++ .../appsettings.Development.json | 28 +++ .../ExchangeRateUpdater.API/appsettings.json | 27 +++ .../ExchangeRateUpdater.Abstraction.csproj | 9 + .../Interfaces/IExchangeRateClient.cs | 18 ++ .../Interfaces/IExchangeRateProvider.cs} | 15 +- .../Interfaces/IExchangeRateService.cs | 22 +++ .../Model/Currency.cs | 48 ++++++ .../Model/ExchangeRate.cs | 55 ++++++ .../CnbApiInfo.md | 64 +++++++ .../DTO/ExchangeRateEntry.cs | 36 ++++ .../DTO/ExchangeRatesResponse.cs | 18 ++ .../ExchangeRateUpdater.CbnApiClient.csproj | 17 ++ .../ExchangeRateCbnApiClient.cs | 68 ++++++++ .../Mapper/CnbExchangeRateMapperExtensions.cs | 53 ++++++ .../DependencyInyection.md | 16 ++ .../ExchangeRateCnbServicesExtension.cs | 37 ++++ ...angeRateUpdater.DependencyInjection.csproj | 22 +++ .../ExchangeRateProvider.cs | 59 +++++++ .../ExchangeRateService.cs | 49 ++++++ .../ExchangeRateUpdater.Service.csproj | 18 ++ .../Abstraction/ModelTests.cs | 66 +++++++ .../Api/ExchangeRateControllerTests.cs | 65 +++++++ .../CnbExchangeRateMapperExtensionsTests.cs | 74 ++++++++ .../ExchangeRateCbnApiClientTests.cs | 133 +++++++++++++++ .../ExchangeRateUpdater.Tests.csproj | 32 ++++ .../IntegrationTests/IntegrationTests.cs | 161 ++++++++++++++++++ .../Service/ExchangeRateProviderTests.cs | 67 ++++++++ .../Service/ExchangeRateServiceTests.cs | 90 ++++++++++ jobs/Backend/Task/ArchitectureAndDecisions.md | 33 ++++ jobs/Backend/Task/Currency.cs | 20 --- jobs/Backend/Task/ExchangeRate.cs | 23 --- jobs/Backend/Task/ExchangeRateUpdater.csproj | 6 +- jobs/Backend/Task/ExchangeRateUpdater.sln | 48 +++++- 39 files changed, 1666 insertions(+), 54 deletions(-) create mode 100644 jobs/Backend/ExchangeRateUpdater.API/Controllers/ExchangeRateController.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.API/ExchangeRateUpdater.Api.csproj create mode 100644 jobs/Backend/ExchangeRateUpdater.API/Logs/log-20260215.txt create mode 100644 jobs/Backend/ExchangeRateUpdater.API/Middleware/ExceptionHandlingMiddleware.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.API/Program.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.API/Properties/launchSettings.json create mode 100644 jobs/Backend/ExchangeRateUpdater.API/appsettings.Development.json create mode 100644 jobs/Backend/ExchangeRateUpdater.API/appsettings.json create mode 100644 jobs/Backend/ExchangeRateUpdater.Abstraction/ExchangeRateUpdater.Abstraction.csproj create mode 100644 jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateClient.cs rename jobs/Backend/{Task/ExchangeRateProvider.cs => ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateProvider.cs} (55%) create mode 100644 jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateService.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Abstraction/Model/Currency.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Abstraction/Model/ExchangeRate.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.CbnApiClient/CnbApiInfo.md create mode 100644 jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRateEntry.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRatesResponse.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.CbnApiClient/ExchangeRateUpdater.CbnApiClient.csproj create mode 100644 jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCbnApiClient.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.CbnApiClient/Mapper/CnbExchangeRateMapperExtensions.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.DependencyInjection/DependencyInyection.md create mode 100644 jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateCnbServicesExtension.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateUpdater.DependencyInjection.csproj create mode 100644 jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateProvider.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateService.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateUpdater.Service.csproj create mode 100644 jobs/Backend/ExchangeRateUpdater.Tests/Abstraction/ModelTests.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Tests/Api/ExchangeRateControllerTests.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/CnbExchangeRateMapperExtensionsTests.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/ExchangeRateCbnApiClientTests.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Tests/ExchangeRateUpdater.Tests.csproj create mode 100644 jobs/Backend/ExchangeRateUpdater.Tests/IntegrationTests/IntegrationTests.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateProviderTests.cs create mode 100644 jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateServiceTests.cs create mode 100644 jobs/Backend/Task/ArchitectureAndDecisions.md delete mode 100644 jobs/Backend/Task/Currency.cs delete mode 100644 jobs/Backend/Task/ExchangeRate.cs diff --git a/jobs/Backend/ExchangeRateUpdater.API/Controllers/ExchangeRateController.cs b/jobs/Backend/ExchangeRateUpdater.API/Controllers/ExchangeRateController.cs new file mode 100644 index 0000000000..c74879bf67 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.API/Controllers/ExchangeRateController.cs @@ -0,0 +1,31 @@ +using ExchangeRateUpdater.Abstraction.Interfaces; +using ExchangeRateUpdater.Abstraction.Model; +using Microsoft.AspNetCore.Mvc; + +namespace ExchangeRateUpdater.Api.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ExchangeRateController( + IExchangeRateService exchangeRateService, + ILogger logger) : ControllerBase + { + [HttpGet] + public async Task>> GetExchangeRates([FromQuery] string currencyCodes) + { + logger.LogInformation("Received request to fetch exchange rates for currencies: {CurrencyCodes}", currencyCodes); + + string[] codes = currencyCodes.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + IEnumerable exchangeRates = await exchangeRateService.GetExchangeRatesFromStringList(codes); + if (exchangeRates.ToList().Count == 0) + { + logger.LogWarning("No exchange rates found for the provided currency codes: {CurrencyCodes}", currencyCodes); + return BadRequest("No exchange rates found for the provided currency codes."); + } + + return Ok(exchangeRates); + + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.API/ExchangeRateUpdater.Api.csproj b/jobs/Backend/ExchangeRateUpdater.API/ExchangeRateUpdater.Api.csproj new file mode 100644 index 0000000000..946f87b13e --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.API/ExchangeRateUpdater.Api.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + + + + + + diff --git a/jobs/Backend/ExchangeRateUpdater.API/Logs/log-20260215.txt b/jobs/Backend/ExchangeRateUpdater.API/Logs/log-20260215.txt new file mode 100644 index 0000000000..4a4f6face3 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.API/Logs/log-20260215.txt @@ -0,0 +1,7 @@ +2026-02-15 18:57:37.727 +01:00 [INF] Starting web host +2026-02-15 18:57:45.283 +01:00 [INF] Received request to fetch exchange rates for currencies: eur +2026-02-15 18:57:45.287 +01:00 [INF] Cache miss for CNB rates, fetching from upstream +2026-02-15 18:57:47.589 +01:00 [INF] Received request to fetch exchange rates for currencies: eur +2026-02-15 18:57:47.589 +01:00 [DBG] Cache hit for CNB rates +2026-02-15 18:58:04.413 +01:00 [INF] Received request to fetch exchange rates for currencies: eur,usd +2026-02-15 18:58:04.413 +01:00 [DBG] Cache hit for CNB rates diff --git a/jobs/Backend/ExchangeRateUpdater.API/Middleware/ExceptionHandlingMiddleware.cs b/jobs/Backend/ExchangeRateUpdater.API/Middleware/ExceptionHandlingMiddleware.cs new file mode 100644 index 0000000000..376fd04db1 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.API/Middleware/ExceptionHandlingMiddleware.cs @@ -0,0 +1,70 @@ +using System.Net; +using System.Text.Json; +using Microsoft.AspNetCore.Mvc; + +namespace ExchangeRateUpdater.Api.Middleware +{ + /// + /// Middleware that handles exceptions thrown during the processing of HTTP requests, logging the error and returning a + /// standardized JSON response. + /// + /// The delegate that represents the next middleware in the request processing pipeline. + /// The logger used to log unhandled exceptions that occur during request processing. + /// The web host environment that provides information about the hosting environment, such as whether the application is + /// in development mode. + public class ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger, IWebHostEnvironment env) + { + private readonly Dictionary exceptionStatusCodes = new() + { + { typeof(ArgumentException), HttpStatusCode.BadRequest }, + { typeof(ArgumentNullException), HttpStatusCode.BadRequest }, + { typeof(InvalidOperationException), HttpStatusCode.InternalServerError } + }; + + /// + /// Invokes the next middleware in the HTTP request pipeline asynchronously and handles any exceptions that + /// occur during execution. + /// + /// The HttpContext for the current request, which encapsulates all HTTP-specific information about an + /// individual HTTP request. + /// A task that represents the asynchronous operation of invoking the next middleware and handling exceptions. + public async Task InvokeAsync(HttpContext context) + { + try + { + await next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + } + } + + /// + /// Handles exceptions that occur during the processing of an HTTP request by logging the error and generating a + /// standardized JSON error response. + /// + /// The HttpContext for the current HTTP request, used to set the response status code and write the error + /// response. + /// The exception that was thrown during request processing. + /// A task that represents the asynchronous operation. + private async Task HandleExceptionAsync(HttpContext context, Exception ex) + { + logger.LogError(ex, "Unhandled exception"); + + context.Response.ContentType = "application/json"; + var status = (int)exceptionStatusCodes.GetValueOrDefault(ex.GetType(), HttpStatusCode.InternalServerError); + context.Response.StatusCode = status; + + var problem = new ProblemDetails + { + Status = status, + Title = status == 500 ? "An internal error occurred." : "A request error occurred.", + Detail = env.IsDevelopment() ? ex.ToString() : null + }; + + var json = JsonSerializer.Serialize(problem); + await context.Response.WriteAsync(json); + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.API/Program.cs b/jobs/Backend/ExchangeRateUpdater.API/Program.cs new file mode 100644 index 0000000000..0ac88b0da4 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.API/Program.cs @@ -0,0 +1,55 @@ +using ExchangeRateUpdater.Api.Middleware; +using ExchangeRateUpdater.DependencyInjection; +using Serilog; + + +var builder = WebApplication.CreateBuilder(args); + +// Configure Serilog from configuration (appsettings.json) +Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(builder.Configuration) + .Enrich.FromLogContext() + .CreateLogger(); + +builder.Host.UseSerilog(); + +try +{ + Log.Information("Starting web host"); + + // Add services to the container. + builder.Services.AddControllers(); + builder.Services.AddExchangeRateServices(); + + // Add Swagger services + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + + + var app = builder.Build(); + + if (app.Environment.IsDevelopment()) + { + // Enable Swagger UI in development + app.UseSwagger(); + app.UseSwaggerUI(); + } + + app.UseMiddleware(); + + app.UseHttpsRedirection(); + + app.MapControllers(); + + app.Run(); +} +catch (Exception ex) +{ + // Catch startup exceptions + Log.Fatal(ex, "Host terminated unexpectedly"); + throw; +} +finally +{ + Log.CloseAndFlush(); +} diff --git a/jobs/Backend/ExchangeRateUpdater.API/Properties/launchSettings.json b/jobs/Backend/ExchangeRateUpdater.API/Properties/launchSettings.json new file mode 100644 index 0000000000..830eaee6a7 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.API/Properties/launchSettings.json @@ -0,0 +1,37 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5165" + }, + "https": { + "commandName": "Project", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7059;http://localhost:5165" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "launchUrl": "swagger" + } + }, + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:60161/", + "sslPort": 44391 + } + } +} \ No newline at end of file diff --git a/jobs/Backend/ExchangeRateUpdater.API/appsettings.Development.json b/jobs/Backend/ExchangeRateUpdater.API/appsettings.Development.json new file mode 100644 index 0000000000..8040cec346 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.API/appsettings.Development.json @@ -0,0 +1,28 @@ +{ + "Serilog": { + "Using": [], + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "WriteTo": [ + { "Name": "Console" }, + { + "Name": "File", + "Args": { + "path": "Logs/log-.txt", + "rollingInterval": "Day", + "shared": true + } + } + ], + "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ], + "Properties": { + "Application": "ExchangeRateUpdater.API" + } + }, + "CbnApiUrl": "https://api.cnb.cz/cnbapi/exrates/daily" +} diff --git a/jobs/Backend/ExchangeRateUpdater.API/appsettings.json b/jobs/Backend/ExchangeRateUpdater.API/appsettings.json new file mode 100644 index 0000000000..f66ebb2227 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.API/appsettings.json @@ -0,0 +1,27 @@ +{ + "Serilog": { + "Using": [], + "MinimumLevel": { + "Default": "Warning", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": "Logs/log-.txt", + "rollingInterval": "Day", + "shared": true + } + } + ], + "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ], + "Properties": { + "Application": "ExchangeRateUpdater.API" + } + }, + "CbnApiUrl": "https://api.cnb.cz/cnbapi/exrates/daily" +} diff --git a/jobs/Backend/ExchangeRateUpdater.Abstraction/ExchangeRateUpdater.Abstraction.csproj b/jobs/Backend/ExchangeRateUpdater.Abstraction/ExchangeRateUpdater.Abstraction.csproj new file mode 100644 index 0000000000..b760144708 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Abstraction/ExchangeRateUpdater.Abstraction.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateClient.cs b/jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateClient.cs new file mode 100644 index 0000000000..4b668d485c --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateClient.cs @@ -0,0 +1,18 @@ +using ExchangeRateUpdater.Abstraction.Model; + +namespace ExchangeRateUpdater.Abstraction.Interfaces +{ + /// + /// Provides an abstraction for retrieving exchange rates for specified currencies from an external source. + /// + public interface IExchangeRateClient + { + /// + /// Retrieves the current exchange rates for the specified currencies from an external source. + /// + /// An optional date parameter to specify the date for which the exchange rates should be valid. If not provided, the method will + /// A task that represents the asynchronous operation. The task result contains an enumerable collection of + /// ExchangeRate objects. + Task> GetExchangeRatesAsync(DateTime? validDate = null); + } +} diff --git a/jobs/Backend/Task/ExchangeRateProvider.cs b/jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateProvider.cs similarity index 55% rename from jobs/Backend/Task/ExchangeRateProvider.cs rename to jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateProvider.cs index 6f82a97fbe..e3e48837a5 100644 --- a/jobs/Backend/Task/ExchangeRateProvider.cs +++ b/jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateProvider.cs @@ -1,9 +1,11 @@ -using System.Collections.Generic; -using System.Linq; +using ExchangeRateUpdater.Abstraction.Model; -namespace ExchangeRateUpdater +namespace ExchangeRateUpdater.Abstraction.Interfaces { - public class ExchangeRateProvider + /// + /// Represents a provider that supplies exchange rates for specified currencies as defined by an external source. + /// + public interface IExchangeRateProvider { /// /// Should return exchange rates among the specified currencies that are defined by the source. But only those defined @@ -11,9 +13,6 @@ public class ExchangeRateProvider /// do not return exchange rate "USD/CZK" with value calculated as 1 / "CZK/USD". If the source does not provide /// some of the currencies, ignore them. /// - public IEnumerable GetExchangeRates(IEnumerable currencies) - { - return Enumerable.Empty(); - } + Task> GetExchangeRatesAsync(IEnumerable currencies); } } diff --git a/jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateService.cs b/jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateService.cs new file mode 100644 index 0000000000..f02c7473c9 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Abstraction/Interfaces/IExchangeRateService.cs @@ -0,0 +1,22 @@ +using ExchangeRateUpdater.Abstraction.Model; + +namespace ExchangeRateUpdater.Abstraction.Interfaces +{ + /// + /// Provides methods to retrieve exchange rates for specified currency codes. + /// + public interface IExchangeRateService + { + /// + /// Retrieves a collection of exchange rates for the specified currency codes. + /// + /// If any of the provided currency codes are invalid, they will be ignored in the + /// result. The method may throw exceptions if the underlying service is unavailable or if there are issues with + /// the provided input. + /// An enumerable collection of currency codes for which exchange rates are to be retrieved. Each code must be a + /// valid ISO 4217 currency code. + /// A task that represents the asynchronous operation, containing an enumerable collection of ExchangeRate + /// objects corresponding to the provided currency codes. + Task> GetExchangeRatesFromStringList(IEnumerable currencyCodes); + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Abstraction/Model/Currency.cs b/jobs/Backend/ExchangeRateUpdater.Abstraction/Model/Currency.cs new file mode 100644 index 0000000000..e1db68ce61 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Abstraction/Model/Currency.cs @@ -0,0 +1,48 @@ +namespace ExchangeRateUpdater.Abstraction.Model +{ + public class Currency + { + /// + /// Initializes a new instance of the Currency class using the specified currency code. + /// + /// The currency code to represent. This value must be a non-empty string consisting of exactly three letters (e.g., "USD", "EUR"). It must not contain any whitespace or non-letter + /// characters. The code is trimmed and converted to uppercase. + /// Thrown if the specified is null, empty, or consists only of white-space characters. + /// Thrown if the specified does not have exactly three characters. + /// Thrown if the specified contains non-letter characters. + public Currency(string code) + { + if (string.IsNullOrWhiteSpace(code)) + throw new ArgumentException("Currency code must not be empty.", nameof(code)); + + code = code.Trim(); + if (code.Length != 3) + throw new ArgumentException("Currency code must be exactly three characters long.", nameof(code)); + + if (!code.All(char.IsLetter)) + throw new ArgumentException("Currency code must consist of letters only.", nameof(code)); + + Code = code.Trim().ToUpperInvariant(); + } + + /// + /// Three-letter ISO 4217 code of the currency. + /// + public string Code { get; } + + public override string ToString() + { + return Code; + } + + // Compare using OrdinalIgnoreCase so the comparison is + // culture-invariant and consistent across environments + // (e.g. Turkish dotted/dotless i behaviour). + public bool Equals(Currency other) => string.Equals(Code, other.Code, StringComparison.OrdinalIgnoreCase); + + public override bool Equals(object? obj) => obj is Currency other && Equals(other); + + // Use the same comparer for hash code to respect equality contract. + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Code); + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Abstraction/Model/ExchangeRate.cs b/jobs/Backend/ExchangeRateUpdater.Abstraction/Model/ExchangeRate.cs new file mode 100644 index 0000000000..7dc4d53705 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Abstraction/Model/ExchangeRate.cs @@ -0,0 +1,55 @@ +namespace ExchangeRateUpdater.Abstraction.Model +{ + /// + /// Represents an exchange rate between two currencies, providing the source currency, target currency, and the + /// exchange rate value. + /// + public class ExchangeRate + { + + /// + /// Initializes a new instance of the ExchangeRate class using the specified source and target currencies and + /// the exchange rate value. + /// + /// The currency from which the exchange rate is calculated. + /// The currency to which the exchange rate is applied. + /// The exchange rate value. Must be greater than zero. + /// Thrown when the value parameter is less than or equal to zero. + public ExchangeRate(Currency sourceCurrency, Currency targetCurrency, decimal value, DateTime validFor) + { + if (value <= 0) + throw new ArgumentOutOfRangeException(nameof(value), "Exchange rate value must be greater than zero."); + + SourceCurrency = sourceCurrency; + TargetCurrency = targetCurrency; + Value = value; + ValidFor = validFor; + } + + /// + /// Gets the currency used as the source for conversion. + /// + public Currency SourceCurrency { get; } + + /// + /// Gets the currency to which the transaction amount will be converted. + /// + public Currency TargetCurrency { get; } + + /// + /// Gets the current exchange rate value represented by this instance. + /// How much of the target currency is equivalent to one unit of the source currency. + /// + public decimal Value { get; } + + /// + /// Gets the date and time until which the current entity is valid. + /// + public DateTime ValidFor { get; } + + public override string ToString() + { + return $"{SourceCurrency}/{TargetCurrency}={Value} (valid for {ValidFor})"; + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/CnbApiInfo.md b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/CnbApiInfo.md new file mode 100644 index 0000000000..9c02e9b167 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/CnbApiInfo.md @@ -0,0 +1,64 @@ +# CNB API - Daily Exchange Rates + +Base URL + +- `https://api.cnb.cz/cnbapi/exrates/daily` + +Description + +- This endpoint returns the full daily foreign exchange rates published by the Czech National Bank (CNB). +- The endpoint does not support filtering by currency: every request returns the whole set of available currency entries. +- Each entry contains a `validFor` property that indicates the date the rate is valid for. + +Update schedule & behaviour + +- Rates are published/updated on working days (weekdays) around 14:30 UTC (I will asume its Zulu time since they do not specify) according to the CNB documentation: https://www.cnb.cz/en/faq/Format-of-the-foreign-exchange-market-rates. +- If the API is queried on a weekend or holiday, the response contains the most recently published rates (the latest `validFor` date prior to the request). + +Response shape (representative) + +```json +{ + "rates": [ + { + "amount": 0, + "country": "string", + "currency": "string", + "currencyCode": "string", + "order": 0, + "rate": 0, + "validFor": "2026-02-15" + } + ] +} +``` + +Field meanings + +- `amount` (decimal): how many units of the foreign currency the `rate` corresponds to. +- `rate` (decimal): amount of CZK for the given `amount` units of the foreign currency. + - To obtain the per-1-unit rate in CZK: `rate / amount`. +- `currencyCode` (string): three-letter ISO 4217 currency code (e.g. `USD`, `EUR`). +- `currency` (string): localized currency name. +- `country` (string): country name associated with the currency. +- `order` (int): ordering index in the returned list (not used for calculations). +- `validFor` (date string): the calendar date that this rate is valid for (format `yyyy-MM-dd`). + +Errors + +- Error responses follow a small shape similar to: + +```json +{ + "description": "...", + "endPoint": "...", + "errorCode": "INTERNAL_SERVER_ERROR", + "happenedAt": "2026-02-15T15:46:07.643Z", + "messageId": "..." +} +``` + +References + +- CNB rates format FAQ: https://www.cnb.cz/en/faq/Format-of-the-foreign-exchange-market-rates +- CNB daily rates endpoint (used): https://api.cnb.cz/cnbapi/exrates/daily diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRateEntry.cs b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRateEntry.cs new file mode 100644 index 0000000000..01872fbc4b --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRateEntry.cs @@ -0,0 +1,36 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace ExchangeRateUpdater.CbnApiClient.DTO +{ + /// + /// Represents an individual exchange rate entry as returned by the CBN API. + /// Each entry contains details about the currency, its exchange rate, and the date for which the rate is valid. + /// + [ExcludeFromCodeCoverage] + public class ExchangeRateEntry + { + [JsonPropertyName("amount")] + public decimal Amount { get; set; } + + [JsonPropertyName("country")] + public string Country { get; set; } = string.Empty; + + [JsonPropertyName("currency")] + public string Currency { get; set; } = string.Empty; + + [JsonPropertyName("currencyCode")] + public string CurrencyCode { get; set; } = string.Empty; + + [JsonPropertyName("order")] + public int Order { get; set; } + + [JsonPropertyName("rate")] + public decimal Rate { get; set; } + + // Date the rate is valid for. The API returns a date string (e.g. "2026-02-15"). + [JsonPropertyName("validFor")] + public DateTime ValidFor { get; set; } + + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRatesResponse.cs b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRatesResponse.cs new file mode 100644 index 0000000000..dd2b866fbf --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRatesResponse.cs @@ -0,0 +1,18 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace ExchangeRateUpdater.CbnApiClient.DTO +{ + /// + /// Represents the response returned by the CBN API containing a collection of exchange rate entries. + /// + [ExcludeFromCodeCoverage] + public class ExchangeRatesResponse + { + /// + /// Collection of rate entries returned by the CBN API. + /// + [JsonPropertyName("rates")] + public List Rates { get; set; } = []; + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/ExchangeRateUpdater.CbnApiClient.csproj b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/ExchangeRateUpdater.CbnApiClient.csproj new file mode 100644 index 0000000000..33984a114e --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/ExchangeRateUpdater.CbnApiClient.csproj @@ -0,0 +1,17 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCbnApiClient.cs b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCbnApiClient.cs new file mode 100644 index 0000000000..08be3d441e --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCbnApiClient.cs @@ -0,0 +1,68 @@ +using ExchangeRateUpdater.Abstraction.Interfaces; +using ExchangeRateUpdater.Abstraction.Model; +using ExchangeRateUpdater.CbnApiClient.DTO; +using ExchangeRateUpdater.CbnApiClient.Mapper; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using System.Text.Json; + +namespace ExchangeRateUpdater.CbnApiClient.Implementation +{ + public class ExchangeRateCbnApiClient(IHttpClientFactory httpClientFactory, IConfiguration config, ILogger logger) : IExchangeRateClient + { + private readonly string CnbApiUrl = config.GetValue("CbnApiUrl") ?? throw new InvalidOperationException("CbnApiUrl configuration is missing."); + // Supports EN and CZ, CZ is the default if not specified. + private readonly string CnbLangToRequest = "EN"; + private readonly string CbnApiClientName = "CbnApi"; + + /// + /// Fetches exchange rates from the CBN API for the specified date. If no date is provided, it fetches today's rates. + /// + /// The date for which to fetch exchange rates. If null, today's rates will be fetched. + /// A collection of objects representing the exchange rates for the specified date. + /// Thrown when the HTTP request to the CBN API fails. + /// Thrown when the response from the CBN API is empty or cannot be deserialized. + public async Task> GetExchangeRatesAsync(DateTime? validDate = null) + { + // prepare the url, add today date as yyyy-MM-dd if specified and lang to "EN" + string urlWithParams = $"{CnbApiUrl}?lang={CnbLangToRequest}"; + // if no date is specified, the CBN API will return today as default + if (validDate.HasValue) + { + urlWithParams += $"&date={validDate.Value:yyyy-MM-dd}"; + } + // make the request to the CBN API using the named client so configured handlers/policies are applied + HttpClient httpClient = httpClientFactory.CreateClient(CbnApiClientName); + using HttpResponseMessage response = await httpClient.GetAsync(urlWithParams); + if (!response.IsSuccessStatusCode) + { + throw new HttpRequestException($"Failed to fetch exchange rates from CBN API. Status code: {response.StatusCode}"); + } + + // check content is not empty + string content = await response.Content.ReadAsStringAsync(); + if (string.IsNullOrWhiteSpace(content)) + { + string message = "Received empty response from CBN API."; + logger.LogError(message); + throw new InvalidOperationException(message); + } + + // deserialize the response. JsonSerializer.Deserialize may throw JsonException for invalid JSON; + ExchangeRatesResponse? dtoRates; + try + { + dtoRates = JsonSerializer.Deserialize(content); + } + catch (JsonException jex) + { + logger.LogError(jex, "Failed to deserialize CBN API response JSON."); + throw new InvalidOperationException("Failed to deserialize CBN API response.", jex); + } + + if (dtoRates is null) + throw new InvalidOperationException("Failed to deserialize CBN API response."); + return dtoRates.Rates.ToDomain(); + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Mapper/CnbExchangeRateMapperExtensions.cs b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Mapper/CnbExchangeRateMapperExtensions.cs new file mode 100644 index 0000000000..f10927119c --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Mapper/CnbExchangeRateMapperExtensions.cs @@ -0,0 +1,53 @@ +using ExchangeRateUpdater.CbnApiClient.DTO; +using ExchangeRateUpdater.Abstraction.Model; + +namespace ExchangeRateUpdater.CbnApiClient.Mapper +{ + + /// + /// Extension methods to map CNB API DTOs to domain models. + /// + public static class CnbExchangeRateMapperExtensions + { + private const string TARGET_CURRENCY_CODE = "CZK"; + /// + /// Maps a single to a domain . + /// By convention the CNB API provides rates as an amount of CZK for a given + /// units of the foreign currency. This method maps the entry to an ExchangeRate where the source currency + /// is the foreign currency and the target currency is CZK. The resulting value is the rate for one unit of + /// the source currency (Rate / Amount). + /// + /// The DTO entry to map. + /// The mapped . + public static ExchangeRate ToDomain(this ExchangeRateEntry entry) + { + ArgumentNullException.ThrowIfNull(entry); + + // Avoid division by zero and ensure the amount is positive as expected by the CNB API. + if (entry.Amount <= 0) + throw new ArgumentException("Entry amount must be greater than zero.", nameof(entry)); + + // CNB returns the amount of CZK for 'Amount' units of the foreign currency. + // Normalize to a per-1-unit rate. + var valuePerUnit = entry.Rate / entry.Amount; + + var source = new Currency(entry.CurrencyCode); + var target = new Currency(TARGET_CURRENCY_CODE); + + return new ExchangeRate(source, target, valuePerUnit, entry.ValidFor); + } + + /// + /// Maps a sequence of to . + /// Null entries are ignored. + /// + public static IEnumerable ToDomain(this IEnumerable entries) + { + ArgumentNullException.ThrowIfNull(entries); + + return entries + .Where(e => e is not null) + .Select(e => e.ToDomain()); + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.DependencyInjection/DependencyInyection.md b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/DependencyInyection.md new file mode 100644 index 0000000000..2e49bbba16 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/DependencyInyection.md @@ -0,0 +1,16 @@ +# Dependency Injection Design +## Purpose +This project encapsulates the dependency injection configuration for the exchange rate services. Instead of registering all services directly inside a specific host (for example, the Web API), the setup is extracted into a shared extension method that can be reused by multiple applications. + +The main entry point is the AddExchangeRateServices(IServiceCollection services) extension method, which wires the IExchangeRateProvider implementation and the CNB HTTP client together with basic resilience policies. + +## Why a separate project? +There are several reasons for keeping this registration logic in a dedicated project rather than placing it directly inside the API project: + +- **Separation of concerns**\ +The host projects (API, console, etc.) focus on hosting concerns (endpoints, input/output, configuration), while this project focuses only on how the exchange rate domain and infrastructure are composed. This makes the startup code of each host simpler and easier to read. + +- **Future extensibility**\ +If in the future a new host is introduced (for example, a SOAP service, a Windows service, or a background worker), it can reuse the same AddExchangeRateServices method without needing to replicate the registration logic. + +Centralizing this setup ensures that all consumers benefit from the same resilience configuration and that changes (for example, different retry settings or a different client implementation) can be made in a single place. \ No newline at end of file diff --git a/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateCnbServicesExtension.cs b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateCnbServicesExtension.cs new file mode 100644 index 0000000000..5a4594113f --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateCnbServicesExtension.cs @@ -0,0 +1,37 @@ +using ExchangeRateUpdater.Abstraction.Interfaces; +using ExchangeRateUpdater.CbnApiClient.Implementation; +using ExchangeRateUpdater.Service; +using Microsoft.Extensions.DependencyInjection; +using Polly; +using Polly.Extensions.Http; + +namespace ExchangeRateUpdater.DependencyInjection +{ + /// + /// Provides extension methods for registering exchange rate client services with the dependency injection + /// container. + /// + /// This static class is intended to be used in application startup to ensure that the + /// IExchangeRateClient implementation is available for dependency injection. It simplifies the configuration of + /// exchange rate services by encapsulating the registration logic. + public static class ExchangeRateCnbServicesExtension + { + public static IServiceCollection AddExchangeRateServices(this IServiceCollection services) + { + services.AddMemoryCache(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // Configure a named HttpClient for the CNB API with Polly-based retry policy. + // This will be available as httpClientFactory.CreateClient("CnbApi"). + services.AddHttpClient("CnbApi") + .AddPolicyHandler(HttpPolicyExtensions + .HandleTransientHttpError() + .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))) + ); + + return services; + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateUpdater.DependencyInjection.csproj b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateUpdater.DependencyInjection.csproj new file mode 100644 index 0000000000..a4e7971419 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateUpdater.DependencyInjection.csproj @@ -0,0 +1,22 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateProvider.cs b/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateProvider.cs new file mode 100644 index 0000000000..f0617a6a51 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateProvider.cs @@ -0,0 +1,59 @@ +using ExchangeRateUpdater.Abstraction.Interfaces; +using ExchangeRateUpdater.Abstraction.Model; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; + +namespace ExchangeRateUpdater.Service +{ + /// + /// Provides methods for retrieving exchange rates for specified currencies from an external source. + /// + /// The client used to obtain exchange rate data from an external provider. + /// The logger used to record informational messages and errors related to exchange rate retrieval. + public class ExchangeRateProvider(IExchangeRateClient exchangeRateClient, ILogger logger, IMemoryCache cache) : IExchangeRateProvider + { + private const string CacheKey = "CnbRates"; + + public async Task> GetExchangeRatesAsync(IEnumerable currencies) + { + ArgumentNullException.ThrowIfNull(currencies); + + // Try get from cache first + if (!cache.TryGetValue>(CacheKey, out var cachedRates)) + { + logger.LogInformation("Cache miss for CNB rates, fetching from upstream"); + var fetched = await exchangeRateClient.GetExchangeRatesAsync(); + + // Determine cache expiration: CNB publishes new rates around 14:30 UTC on working days. + // Set 30 minutes margin to ensure we get the new rates after publication. + var now = DateTime.UtcNow; + var nextPublication = new DateTime(now.Year, now.Month, now.Day, 15, 0, 0, DateTimeKind.Utc); + if (now >= nextPublication) + { + // already past today's publication time, schedule for next day + nextPublication = nextPublication.AddDays(1); + } + + var absoluteExpirationRelative = nextPublication - now; + // Guard: ensure a minimum cache duration and reasonable maximum (24h) + // Cache will refresh even on non-working days, but that's acceptable because 1 unnecessary request a day is not a problem for CNB. + if (absoluteExpirationRelative < TimeSpan.FromMinutes(1)) absoluteExpirationRelative = TimeSpan.FromMinutes(1); + if (absoluteExpirationRelative > TimeSpan.FromHours(24)) absoluteExpirationRelative = TimeSpan.FromHours(24); + + var options = new MemoryCacheEntryOptions() + .SetAbsoluteExpiration(absoluteExpirationRelative); + + cache.Set(CacheKey, fetched, options); + cachedRates = fetched; + } + else + { + logger.LogDebug("Cache hit for CNB rates"); + } + + var currencySet = currencies.ToHashSet(); + var filteredRates = cachedRates.Where(rate => currencySet.Contains(rate.SourceCurrency)); + return filteredRates; + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateService.cs b/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateService.cs new file mode 100644 index 0000000000..fdbaf8e51d --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateService.cs @@ -0,0 +1,49 @@ +using ExchangeRateUpdater.Abstraction.Interfaces; +using ExchangeRateUpdater.Abstraction.Model; +using Microsoft.Extensions.Logging; + +namespace ExchangeRateUpdater.Service +{ + /// + /// Provides functionality to retrieve exchange rates for specified currency codes. + /// + /// The provider used to fetch exchange rate data. + /// The logger instance used to record informational and error messages for this service. + public class ExchangeRateService(IExchangeRateProvider exchangeRateProvider, ILogger logger) : IExchangeRateService + { + /// + /// Retrieves exchange rates for the specified collection of currency codes. + /// + /// An enumerable collection of currency code strings to retrieve exchange rates for. Each string must represent + /// a valid currency code. + /// A task that represents the asynchronous operation. The task result contains a collection of exchange rates + /// corresponding to the valid currency codes provided. Returns an empty collection if no valid codes are + /// supplied. + public async Task> GetExchangeRatesFromStringList(IEnumerable currencyCodes) + { + List currencyList = new(); + foreach (var currencyCode in currencyCodes) + { + try + { + var currency = new Currency(currencyCode); + currencyList.Add(currency); + } + catch (ArgumentException ex) + { + logger.LogWarning($"Invalid currency {currencyCode}: {ex.Message}"); + continue; + } + } + + if (currencyList.Count == 0) + { + logger.LogWarning("No valid currency codes provided."); + return []; + } + + IEnumerable exchangeRates = await exchangeRateProvider.GetExchangeRatesAsync(currencyList); + return exchangeRates; + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateUpdater.Service.csproj b/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateUpdater.Service.csproj new file mode 100644 index 0000000000..4efb709549 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateUpdater.Service.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/Abstraction/ModelTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/Abstraction/ModelTests.cs new file mode 100644 index 0000000000..97db54f8d6 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Tests/Abstraction/ModelTests.cs @@ -0,0 +1,66 @@ +using ExchangeRateUpdater.Abstraction.Model; + +namespace ExchangeRateUpdater.Tests.Abstraction +{ + [TestFixture] + public class ModelTests + { + [Test] + public void Currency_Constructor_NormalizesAndToString() + { + var c = new Currency(" usd "); + Assert.That(c.Code, Is.EqualTo("USD")); + Assert.That(c.ToString(), Is.EqualTo("USD")); + } + + [Test] + public void Currency_Constructor_InvalidInputs_Throw() + { + Assert.That(() => new Currency(null!), Throws.TypeOf()); + Assert.That(() => new Currency(string.Empty), Throws.TypeOf()); + Assert.That(() => new Currency(" "), Throws.TypeOf()); + Assert.That(() => new Currency("US"), Throws.TypeOf()); + Assert.That(() => new Currency("USDE"), Throws.TypeOf()); + Assert.That(() => new Currency("U$D"), Throws.TypeOf()); + } + + [Test] + public void Currency_Equals_IsCaseInsensitive() + { + var a = new Currency("usd"); + var b = new Currency("USD"); + + Assert.That(a, Is.EqualTo(b)); + Assert.That(b, Is.EqualTo(a)); + Assert.That(a.GetHashCode(), Is.EqualTo(b.GetHashCode())); + } + + [Test] + public void ExchangeRate_Constructor_SetsProperties() + { + var src = new Currency("USD"); + var tgt = new Currency("CZK"); + var validFor = new DateTime(2026, 02, 15); + + var rate = new ExchangeRate(src, tgt, 25.5m, validFor); + + Assert.That(rate.SourceCurrency, Is.EqualTo(src)); + Assert.That(rate.TargetCurrency, Is.EqualTo(tgt)); + Assert.That(rate.Value, Is.EqualTo(25.5m)); + Assert.That(rate.ValidFor, Is.EqualTo(validFor)); + Assert.That(rate.ToString(), Does.Contain("USD/CZK=")); + Assert.That(rate.ToString(), Does.Contain(validFor.ToString())); + } + + [Test] + public void ExchangeRate_InvalidValue_Throws() + { + var src = new Currency("USD"); + var tgt = new Currency("CZK"); + var validFor = DateTime.UtcNow; + + Assert.That(() => new ExchangeRate(src, tgt, 0m, validFor), Throws.TypeOf()); + Assert.That(() => new ExchangeRate(src, tgt, -1m, validFor), Throws.TypeOf()); + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/Api/ExchangeRateControllerTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/Api/ExchangeRateControllerTests.cs new file mode 100644 index 0000000000..4eab0f3984 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Tests/Api/ExchangeRateControllerTests.cs @@ -0,0 +1,65 @@ +using ExchangeRateUpdater.Abstraction.Interfaces; +using ExchangeRateUpdater.Abstraction.Model; +using ExchangeRateUpdater.Api.Controllers; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using NSubstitute; + +namespace ExchangeRateUpdater.Tests.Api +{ + [TestFixture] + internal class ExchangeRateControllerTests + { + private IExchangeRateService _service = null!; + private ILogger _logger = null!; + private ExchangeRateController _controller = null!; + + [SetUp] + public void SetUp() + { + _service = Substitute.For(); + _logger = Substitute.For>(); + _controller = new ExchangeRateController(_service, _logger); + } + + [Test] + public async Task GetExchangeRates_ReturnsOkWithRates_WhenServiceReturnsRates() + { + var now = DateTime.UtcNow; + var rates = new[] { new ExchangeRate(new Currency("USD"), new Currency("CZK"), 25m, now) }; + _service.GetExchangeRatesFromStringList(Arg.Any>()).Returns(Task.FromResult>(rates)); + + var result = await _controller.GetExchangeRates("USD, EUR"); + + Assert.That(result.Result, Is.TypeOf()); + var ok = (OkObjectResult)result.Result!; + Assert.That(ok.Value, Is.InstanceOf>()); + var returned = ((IEnumerable)ok.Value!).ToArray(); + Assert.That(returned.Length, Is.EqualTo(1)); + Assert.That(returned[0].SourceCurrency.Code, Is.EqualTo("USD")); + + // Ensure service was called with the split and trimmed values + await _service.Received(1).GetExchangeRatesFromStringList(Arg.Is>(s => s.SequenceEqual(new[] { "USD", "EUR" }))); + } + + [Test] + public async Task GetExchangeRates_ReturnsBadRequest_WhenNoRates() + { + _service.GetExchangeRatesFromStringList(Arg.Any>()).Returns(Task.FromResult>(Array.Empty())); + + var result = await _controller.GetExchangeRates("USD"); + + Assert.That(result.Result, Is.TypeOf()); + var bad = (BadRequestObjectResult)result.Result!; + Assert.That(bad.Value, Is.EqualTo("No exchange rates found for the provided currency codes.")); + + // Logger should have been called to warn + _logger.Received().Log( + LogLevel.Warning, + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any>()); + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/CnbExchangeRateMapperExtensionsTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/CnbExchangeRateMapperExtensionsTests.cs new file mode 100644 index 0000000000..9b4cb07e6f --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/CnbExchangeRateMapperExtensionsTests.cs @@ -0,0 +1,74 @@ +using ExchangeRateUpdater.CbnApiClient.DTO; +using ExchangeRateUpdater.CbnApiClient.Mapper; + +namespace ExchangeRateUpdater.Tests.CbnApiClient +{ + [TestFixture] + public class CnbExchangeRateMapperExtensionsTests + { + [Test] + public void ToDomain_SingleEntry_MapsCorrectly() + { + var dto = new ExchangeRateEntry + { + Amount = 100, + Rate = 2500m, + CurrencyCode = "USD", + ValidFor = new DateTime(2026, 2, 15) + }; + + var domain = dto.ToDomain(); + + Assert.That(domain.SourceCurrency.Code, Is.EqualTo("USD")); + Assert.That(domain.TargetCurrency.Code, Is.EqualTo("CZK")); + Assert.That(domain.Value, Is.EqualTo(25.0m)); + Assert.That(domain.ValidFor, Is.EqualTo(new DateTime(2026, 2, 15))); + } + + [Test] + public void ToDomain_SingleEntry_Null_Throws() + { + ExchangeRateEntry? dto = null; + Assert.That(() => CnbExchangeRateMapperExtensions.ToDomain(dto!), Throws.TypeOf()); + } + + [Test] + public void ToDomain_SingleEntry_AmountZero_Throws() + { + var dto = new ExchangeRateEntry + { + Amount = 0, + Rate = 100m, + CurrencyCode = "EUR", + ValidFor = DateTime.UtcNow + }; + + Assert.That(() => dto.ToDomain(), Throws.TypeOf()); + } + + [Test] + public void ToDomain_Enumerable_MapsAndIgnoresNulls() + { + var list = new List + { + new ExchangeRateEntry { Amount = 1, Rate = 30m, CurrencyCode = "ABC", ValidFor = new DateTime(2026,1,1) }, + null, + new ExchangeRateEntry { Amount = 2, Rate = 100m, CurrencyCode = "DEF", ValidFor = new DateTime(2026,1,2) } + }; + + var mapped = CnbExchangeRateMapperExtensions.ToDomain(list.Where(e => e is not null).Select(e => e!)); + + var arr = mapped.ToArray(); + Assert.That(arr.Length, Is.EqualTo(2)); + Assert.That(arr[0].SourceCurrency.Code, Is.EqualTo("ABC")); + Assert.That(arr[1].SourceCurrency.Code, Is.EqualTo("DEF")); + } + + [Test] + public void ToDomain_Enumerable_Null_Throws() + { + IEnumerable? entries = null; + Assert.That(() => CnbExchangeRateMapperExtensions.ToDomain(entries!), Throws.TypeOf()); + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/ExchangeRateCbnApiClientTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/ExchangeRateCbnApiClientTests.cs new file mode 100644 index 0000000000..a688e3234d --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/ExchangeRateCbnApiClientTests.cs @@ -0,0 +1,133 @@ +using System.Net; +using ExchangeRateUpdater.CbnApiClient.Implementation; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using NSubstitute; + +namespace ExchangeRateUpdater.Tests.CbnApiClient +{ + public class ExchangeRateCbnApiClientTests + { + private IConfiguration _config = null!; + private readonly ILogger _logger = NullLogger.Instance; + + [SetUp] + public void SetUp() + { + _config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { { "CbnApiUrl", "https://api.cnb.cz/cnbapi/exrates/daily" } }) + .Build(); + } + + private static IHttpClientFactory CreateHttpClientFactory(HttpResponseMessage response, Action? inspect = null) + { + // Create an in-memory HttpClient that uses a delegating handler returning the provided response. + // Do not set a real BaseAddress to avoid any accidental network usage in tests. + var handler = new DelegatingHandlerStub(response, inspect); + var client = new HttpClient(handler); + + // Use NSubstitute to create a test double for IHttpClientFactory that returns our client. + var factory = Substitute.For(); + factory.CreateClient(Arg.Any()).Returns(client); + return factory; + } + + [Test] + public async Task GetExchangeRatesAsync_ReturnsMappedRates() + { + var json = "{\"rates\":[{\"amount\":100,\"country\":\"United States\",\"currency\":\"Dollar\",\"currencyCode\":\"USD\",\"order\":1,\"rate\":2500,\"validFor\":\"2026-02-15\"}]}"; + var response = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(json) + }; + + var factory = CreateHttpClientFactory(response); + + var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + var rates = await client.GetExchangeRatesAsync(); + + Assert.That(rates, Is.Not.Null); + var first = rates.First(); + Assert.That(first.SourceCurrency.Code, Is.EqualTo("USD")); + Assert.That(first.TargetCurrency.Code, Is.EqualTo("CZK")); + Assert.That(first.Value, Is.EqualTo(25.0m)); + Assert.That(first.ValidFor, Is.EqualTo(new DateTime(2026, 02, 15))); + } + + [Test] + public void GetExchangeRatesAsync_ThrowsOnNonSuccess() + { + var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); + var factory = CreateHttpClientFactory(response); + + var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + Assert.That(async () => await client.GetExchangeRatesAsync(), Throws.InstanceOf()); + } + + [Test] + public void GetExchangeRatesAsync_ThrowsOnEmptyContent() + { + var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(string.Empty) }; + var factory = CreateHttpClientFactory(response); + + var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + Assert.That(async () => await client.GetExchangeRatesAsync(), Throws.InstanceOf()); + } + + [Test] + public void GetExchangeRatesAsync_ThrowsOnInvalidJson() + { + var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("not json") }; + var factory = CreateHttpClientFactory(response); + + var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + Assert.That(async () => await client.GetExchangeRatesAsync(), Throws.InstanceOf()); + } + + [Test] + public async Task GetExchangeRatesAsync_IncludesDateParameterWhenRequested() + { + HttpRequestMessage? captured = null; + var json = "{\"rates\":[]}"; + var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(json) }; + var factory = CreateHttpClientFactory(response, req => captured = req); + + var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + var date = new DateTime(2026, 02, 15); + await client.GetExchangeRatesAsync(date); + + Assert.That(captured, Is.Not.Null); + Assert.That(captured!.RequestUri!.ToString(), Does.Contain("date=2026-02-15")); + } + } + + // Simple IHttpClientFactory implementation for tests + internal class SimpleHttpClientFactory : IHttpClientFactory + { + private readonly HttpClient _client; + + public SimpleHttpClientFactory(HttpClient client) => _client = client; + + public HttpClient CreateClient(string name) => _client; + } + + // DelegatingHandler stub that returns a configured response and allows inspection of the request + internal class DelegatingHandlerStub : DelegatingHandler + { + private readonly HttpResponseMessage _response; + private readonly Action? _inspect; + + public DelegatingHandlerStub(HttpResponseMessage response, Action? inspect = null) + { + _response = response; + _inspect = inspect; + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + _inspect?.Invoke(request); + return Task.FromResult(_response); + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/ExchangeRateUpdater.Tests.csproj b/jobs/Backend/ExchangeRateUpdater.Tests/ExchangeRateUpdater.Tests.csproj new file mode 100644 index 0000000000..a1111e3bb4 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Tests/ExchangeRateUpdater.Tests.csproj @@ -0,0 +1,32 @@ + + + + net10.0 + latest + enable + enable + false + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/IntegrationTests/IntegrationTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/IntegrationTests/IntegrationTests.cs new file mode 100644 index 0000000000..98617d8b79 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Tests/IntegrationTests/IntegrationTests.cs @@ -0,0 +1,161 @@ +using System.Net; +using System.Text.Json; +using ExchangeRateUpdater.Abstraction.Model; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; + +namespace ExchangeRateUpdater.Tests.IntegrationTests +{ + [TestFixture] + internal class IntegrationTests + { + private WebApplicationFactory _factory = null!; + private HttpClient _apiClient = null!; + + [SetUp] + public void SetUp() + { + // Reset the call counter before each test + MockCnbApiHandler.ResetCallCount(); + + _factory = new WebApplicationFactory() + .WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => + { + // Remove the real named HttpClient registration for "CbnApi" if already registered + var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IHttpClientFactory)); + if (descriptor != null) + { + services.Remove(descriptor); + } + + // Register a mock HttpClient that returns a controlled response + services.AddSingleton(sp => new MockHttpClientFactory()); + }); + }); + + _apiClient = _factory.CreateClient(); + } + + [TearDown] + public void TearDown() + { + _apiClient?.Dispose(); + _factory?.Dispose(); + } + + [Test] + public async Task GetExchangeRates_EndToEnd_ReturnsFilteredRates() + { + // Call the actual API endpoint + var response = await _apiClient.GetAsync("/ExchangeRate?currencyCodes=USD,EUR"); + + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + + var content = await response.Content.ReadAsStringAsync(); + var rates = JsonSerializer.Deserialize>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.That(rates, Is.Not.Null); + Assert.That(rates!.Count, Is.GreaterThan(0)); + + // Verify that returned rates include USD and/or EUR (depends on mock response) + var codes = rates.Select(r => r.SourceCurrency.Code).ToList(); + Assert.That(codes, Does.Contain("USD").Or.Contain("EUR")); + } + + [Test] + public async Task GetExchangeRates_InvalidCurrencyCodes_ReturnsBadRequest() + { + // Request with invalid codes (e.g., empty, too short) + var response = await _apiClient.GetAsync("/ExchangeRate?currencyCodes=US,E"); + + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest).Or.EqualTo(HttpStatusCode.OK)); + } + + [Test] + public async Task GetExchangeRates_CachingWorks_OnlyOneUpstreamCall() + { + // First call + var response1 = await _apiClient.GetAsync("/ExchangeRate?currencyCodes=USD"); + Assert.That(response1.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + + // Verify one upstream call was made + Assert.That(MockCnbApiHandler.CallCount, Is.EqualTo(1)); + + // Second call (should hit cache if enabled) + var response2 = await _apiClient.GetAsync("/ExchangeRate?currencyCodes=EUR"); + Assert.That(response2.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + + // Verify still only one upstream call was made (second request hit cache) + Assert.That(MockCnbApiHandler.CallCount, Is.EqualTo(1), "Cache should prevent a second upstream call"); + } + } + + // Mock IHttpClientFactory that returns a mock HttpClient with a delegating handler + internal class MockHttpClientFactory : IHttpClientFactory + { + public HttpClient CreateClient(string name) + { + var handler = new MockCnbApiHandler(); + return new HttpClient(handler); + } + } + + // Mock delegating handler that returns a fake CNB API response + internal class MockCnbApiHandler : DelegatingHandler + { + private static int _callCount = 0; + + public static int CallCount => _callCount; + + public static void ResetCallCount() => _callCount = 0; + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + // Increment the call counter + Interlocked.Increment(ref _callCount); + + // Return a fake JSON response similar to CNB API + var fakeResponse = @"{ + ""rates"": [ + { + ""amount"": 1, + ""country"": ""United States"", + ""currency"": ""Dollar"", + ""currencyCode"": ""USD"", + ""order"": 1, + ""rate"": 25.5, + ""validFor"": ""2026-02-15"" + }, + { + ""amount"": 1, + ""country"": ""Eurozone"", + ""currency"": ""Euro"", + ""currencyCode"": ""EUR"", + ""order"": 2, + ""rate"": 27.0, + ""validFor"": ""2026-02-15"" + }, + { + ""amount"": 100, + ""country"": ""Japan"", + ""currency"": ""Yen"", + ""currencyCode"": ""JPY"", + ""order"": 3, + ""rate"": 22.0, + ""validFor"": ""2026-02-15"" + } + ] +}"; + + var response = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(fakeResponse, System.Text.Encoding.UTF8, "application/json") + }; + + return Task.FromResult(response); + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateProviderTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateProviderTests.cs new file mode 100644 index 0000000000..e052d09e7f --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateProviderTests.cs @@ -0,0 +1,67 @@ +using ExchangeRateUpdater.Abstraction.Interfaces; +using ExchangeRateUpdater.Abstraction.Model; +using ExchangeRateUpdater.Service; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging.Abstractions; +using NSubstitute; + +namespace ExchangeRateUpdater.Tests.Service +{ + [TestFixture] + public class ExchangeRateProviderTests + { + private IExchangeRateClient _client = null!; + private IMemoryCache _cache = null!; + + [SetUp] + public void SetUp() + { + _client = Substitute.For(); + _cache = new MemoryCache(new MemoryCacheOptions()); + } + + [TearDown] + public void TearDown() + { + _cache.Dispose(); + } + + [Test] + public async Task GetExchangeRatesAsync_FetchesFromClientAndCaches() + { + var now = DateTime.UtcNow; + var usd = new ExchangeRate(new Currency("USD"), new Currency("CZK"), 25m, now); + var eur = new ExchangeRate(new Currency("EUR"), new Currency("CZK"), 27m, now); + + _client.GetExchangeRatesAsync().Returns(Task.FromResult>(new[] { usd, eur })); + + var provider = new ExchangeRateProvider(_client, NullLogger.Instance, _cache); + + var requested = new[] { new Currency("usd") }; + + var firstCall = await provider.GetExchangeRatesAsync(requested); + Assert.That(firstCall, Is.Not.Null); + var arr1 = firstCall.ToArray(); + Assert.That(arr1.Length, Is.EqualTo(1)); + Assert.That(arr1[0].SourceCurrency.Code, Is.EqualTo("USD")); + + // Client should have been called once + await _client.Received(1).GetExchangeRatesAsync(); + + // Call again: should hit cache and not call client again + var secondCall = await provider.GetExchangeRatesAsync(requested); + await _client.Received(1).GetExchangeRatesAsync(); + var arr2 = secondCall.ToArray(); + Assert.That(arr2.Length, Is.EqualTo(1)); + Assert.That(arr2[0].SourceCurrency.Code, Is.EqualTo("USD")); + } + + [Test] + public void GetExchangeRatesAsync_NullCurrencies_Throws() + { + var provider = new ExchangeRateProvider(_client, NullLogger.Instance, _cache); + + Assert.That(async () => await provider.GetExchangeRatesAsync(null!), Throws.TypeOf()); + } + } +} diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateServiceTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateServiceTests.cs new file mode 100644 index 0000000000..9d94d195f5 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateServiceTests.cs @@ -0,0 +1,90 @@ +using ExchangeRateUpdater.Abstraction.Interfaces; +using ExchangeRateUpdater.Abstraction.Model; +using ExchangeRateUpdater.Service; +using Microsoft.Extensions.Logging; +using NSubstitute; + +namespace ExchangeRateUpdater.Tests.Service +{ + [TestFixture] + internal class ExchangeRateServiceTests + { + private IExchangeRateProvider _provider = null!; + private ILogger _logger = null!; + + [SetUp] + public void SetUp() + { + _provider = Substitute.For(); + _logger = Substitute.For>(); + } + + [Test] + public async Task GetExchangeRatesFromStringList_ValidCodes_CallsProviderAndReturnsRates() + { + var now = DateTime.UtcNow; + var expected = new[] { new ExchangeRate(new Currency("USD"), new Currency("CZK"), 25m, now) }; + + _provider.GetExchangeRatesAsync(Arg.Is>(list => list.Select(c => c.Code).SequenceEqual(new[] { "USD" }))) + .Returns(Task.FromResult>(expected)); + + var svc = new ExchangeRateService(_provider, _logger); + var result = await svc.GetExchangeRatesFromStringList(new[] { "USD" }); + + Assert.That(result, Is.Not.Null); + Assert.That(result.ToArray().Length, Is.EqualTo(1)); + Assert.That(result.First().SourceCurrency.Code, Is.EqualTo("USD")); + } + + [Test] + public async Task GetExchangeRatesFromStringList_InvalidCodes_SkipsInvalidAndLogsWarning() + { + var provider = Substitute.For(); + var logger = Substitute.For>(); + + var now = DateTime.UtcNow; + var expected = new[] { new ExchangeRate(new Currency("USD"), new Currency("CZK"), 25m, now) }; + + provider.GetExchangeRatesAsync(Arg.Is>(list => list.Select(c => c.Code).SequenceEqual(new[] { "USD" }))) + .Returns(Task.FromResult>(expected)); + + var svc = new ExchangeRateService(provider, logger); + + // "US" is invalid (length != 3) and should be skipped + var result = await svc.GetExchangeRatesFromStringList(new[] { "USD", "US" }); + + Assert.That(result, Is.Not.Null); + Assert.That(result.First().SourceCurrency.Code, Is.EqualTo("USD")); + + // Verify a warning was logged for the invalid code + logger.Received().Log( + LogLevel.Warning, + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any>()); + } + + [Test] + public async Task GetExchangeRatesFromStringList_NoValidCodes_ReturnsEmptyAndLogsWarning() + { + var provider = Substitute.For(); + var logger = Substitute.For>(); + + var svc = new ExchangeRateService(provider, logger); + + var result = await svc.GetExchangeRatesFromStringList(new[] { "", " " }); + + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.Empty); + + // Should have logged a warning about no valid currency codes + logger.Received().Log( + LogLevel.Warning, + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any>()); + } + } +} diff --git a/jobs/Backend/Task/ArchitectureAndDecisions.md b/jobs/Backend/Task/ArchitectureAndDecisions.md new file mode 100644 index 0000000000..e6ff96ff4a --- /dev/null +++ b/jobs/Backend/Task/ArchitectureAndDecisions.md @@ -0,0 +1,33 @@ +# Architecture & Decisions +Context and goals +This document describes the architecture and key decisions made while implementing the ExchangeRateProvider for the Czech National Bank (CNB). The goal is to build a small but production-ready component that focuses on clarity, testability, and resilience rather than on feature completeness. + +## Target framework choice (.NET 10) + +The original skeleton project was provided targeting .NET 6. For the implementation, I updated the target framework to .NET 10 (`net10.0`) for the following reasons: + +- .NET 10 is a Long Term Support (LTS) release, supported for three years, which makes it a stable choice for production-oriented components. +- It provides the latest runtime and library improvements, including performance and reliability enhancements that benefit long-running services and network-bound components such as an exchange rate provider. + + +## Data source (Czech National Bank) +The provider uses an official public data source published by the Czech National Bank. The chosen endpoint exposes daily exchange rates for multiple currencies against CZK and is suitable for server-side consumption. The implementation assumes the format to be stable and documented, and it includes basic validation to detect unexpected changes in the response structure. + +## Domain model and contracts +The implementation follows the interface and expectations defined in ExchangeRateProvider.cs. The primary responsibility of the provider is to deliver exchange rates for a given date and currency in a strongly-typed way. Any parsing or transport-specific details are kept internal so that consumers only see a simple, domain-focused API. + +## Solution design +The solution is structured around a clear separation of concerns. An HTTP client component is responsible for calling the CNB endpoint and retrieving the raw response. A parser component translates the CNB response format into domain objects that represent exchange rates. The ExchangeRateProvider orchestrates these pieces and exposes a simple method to obtain exchange rates for the rest of the system. This separation makes the code easier to test and maintain. + +## Error handling and resilience +The provider handles common failure scenarios explicitly, such as network errors, timeouts, invalid responses, or missing exchange rates. In these cases, it either throws meaningful exceptions or returns clearly defined results, depending on the contract. Input parameters (such as dates or currency codes) are validated early to avoid ambiguous behavior. Timeouts and basic guards are used to prevent the application from hanging when the external service is slow or unavailable. +​ + +## Testing and validation +The implementation is covered by unit tests focusing on the most critical logic: parsing of the CNB response, correct mapping of raw data to domain models, and behavior in error scenarios (for example, unsupported currencies or malformed responses). External dependencies such as HTTP calls are abstracted behind interfaces so tests can run deterministically without hitting the real CNB endpoint. + +## Maintainability and extensibility +The design aims to make future changes as localized as possible. Adding another bank or data source would typically require introducing a new provider implementation and possibly a new parser, without affecting existing consumers. Configuration points (such as base URLs or timeouts) are kept outside of the core logic so they can be adjusted without code changes. + +## Future improvements +Potential future improvements include caching daily exchange rates to reduce the number of external calls, exposing configuration for retry policies and resilience strategies, and adding support for additional CNB endpoints (for example, historical data or different formats). Another possible enhancement is richer logging around failures to simplify diagnostics in production environments. \ No newline at end of file diff --git a/jobs/Backend/Task/Currency.cs b/jobs/Backend/Task/Currency.cs deleted file mode 100644 index f375776f25..0000000000 --- a/jobs/Backend/Task/Currency.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace ExchangeRateUpdater -{ - public class Currency - { - public Currency(string code) - { - Code = code; - } - - /// - /// Three-letter ISO 4217 code of the currency. - /// - public string Code { get; } - - public override string ToString() - { - return Code; - } - } -} diff --git a/jobs/Backend/Task/ExchangeRate.cs b/jobs/Backend/Task/ExchangeRate.cs deleted file mode 100644 index 58c5bb10e0..0000000000 --- a/jobs/Backend/Task/ExchangeRate.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace ExchangeRateUpdater -{ - public class ExchangeRate - { - public ExchangeRate(Currency sourceCurrency, Currency targetCurrency, decimal value) - { - SourceCurrency = sourceCurrency; - TargetCurrency = targetCurrency; - Value = value; - } - - public Currency SourceCurrency { get; } - - public Currency TargetCurrency { get; } - - public decimal Value { get; } - - public override string ToString() - { - return $"{SourceCurrency}/{TargetCurrency}={Value}"; - } - } -} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.csproj b/jobs/Backend/Task/ExchangeRateUpdater.csproj index 2fc654a12b..1a9fa9d905 100644 --- a/jobs/Backend/Task/ExchangeRateUpdater.csproj +++ b/jobs/Backend/Task/ExchangeRateUpdater.csproj @@ -2,7 +2,11 @@ Exe - net6.0 + net10.0 + + + + \ No newline at end of file diff --git a/jobs/Backend/Task/ExchangeRateUpdater.sln b/jobs/Backend/Task/ExchangeRateUpdater.sln index 89be84daff..267928f2d5 100644 --- a/jobs/Backend/Task/ExchangeRateUpdater.sln +++ b/jobs/Backend/Task/ExchangeRateUpdater.sln @@ -1,10 +1,27 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +# Visual Studio Version 18 +VisualStudioVersion = 18.3.11505.172 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater", "ExchangeRateUpdater.csproj", "{7B2695D6-D24C-4460-A58E-A10F08550CE0}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Elementos de la solución", "Elementos de la solución", "{48B4E715-10EA-4BC9-A470-3772CDD8D796}" + ProjectSection(SolutionItems) = preProject + ArchitectureAndDecisions.md = ArchitectureAndDecisions.md + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.Abstraction", "..\ExchangeRateUpdater.Abstraction\ExchangeRateUpdater.Abstraction.csproj", "{057474C6-1170-4F57-BBE0-6346DCFF15F3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.Api", "..\ExchangeRateUpdater.API\ExchangeRateUpdater.Api.csproj", "{9C0F9D26-C60C-47F4-97EE-44CBAA8B1D70}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.CbnApiClient", "..\ExchangeRateUpdater.CbnApiClient\ExchangeRateUpdater.CbnApiClient.csproj", "{DBF8A019-CB62-4501-853E-A2F880880B03}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.DependencyInjection", "..\ExchangeRateUpdater.DependencyInjection\ExchangeRateUpdater.DependencyInjection.csproj", "{AAA4D713-37BE-48ED-802C-2CB5E7C64C50}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.Service", "..\ExchangeRateUpdater.Service\ExchangeRateUpdater.Service.csproj", "{51ED7477-E620-4969-A450-572C5C4945AC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.Tests", "..\ExchangeRateUpdater.Tests\ExchangeRateUpdater.Tests.csproj", "{EEB3288A-C038-4EAC-877E-7E3862D4D8BA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,8 +32,35 @@ Global {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Debug|Any CPU.Build.0 = Debug|Any CPU {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.ActiveCfg = Release|Any CPU {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.Build.0 = Release|Any CPU + {057474C6-1170-4F57-BBE0-6346DCFF15F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {057474C6-1170-4F57-BBE0-6346DCFF15F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {057474C6-1170-4F57-BBE0-6346DCFF15F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {057474C6-1170-4F57-BBE0-6346DCFF15F3}.Release|Any CPU.Build.0 = Release|Any CPU + {9C0F9D26-C60C-47F4-97EE-44CBAA8B1D70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C0F9D26-C60C-47F4-97EE-44CBAA8B1D70}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C0F9D26-C60C-47F4-97EE-44CBAA8B1D70}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C0F9D26-C60C-47F4-97EE-44CBAA8B1D70}.Release|Any CPU.Build.0 = Release|Any CPU + {DBF8A019-CB62-4501-853E-A2F880880B03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBF8A019-CB62-4501-853E-A2F880880B03}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBF8A019-CB62-4501-853E-A2F880880B03}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBF8A019-CB62-4501-853E-A2F880880B03}.Release|Any CPU.Build.0 = Release|Any CPU + {AAA4D713-37BE-48ED-802C-2CB5E7C64C50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AAA4D713-37BE-48ED-802C-2CB5E7C64C50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AAA4D713-37BE-48ED-802C-2CB5E7C64C50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AAA4D713-37BE-48ED-802C-2CB5E7C64C50}.Release|Any CPU.Build.0 = Release|Any CPU + {51ED7477-E620-4969-A450-572C5C4945AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51ED7477-E620-4969-A450-572C5C4945AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51ED7477-E620-4969-A450-572C5C4945AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51ED7477-E620-4969-A450-572C5C4945AC}.Release|Any CPU.Build.0 = Release|Any CPU + {EEB3288A-C038-4EAC-877E-7E3862D4D8BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEB3288A-C038-4EAC-877E-7E3862D4D8BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEB3288A-C038-4EAC-877E-7E3862D4D8BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEB3288A-C038-4EAC-877E-7E3862D4D8BA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0225AD9B-A542-4599-8171-46F69207D1DE} + EndGlobalSection EndGlobal From f518a9443c78af00003c912e5576ba42b1d6edc7 Mon Sep 17 00:00:00 2001 From: alvaro13bq Date: Mon, 16 Feb 2026 12:29:00 +0100 Subject: [PATCH 2/5] add code coverage report --- jobs/Backend/CodeCoverage.png | Bin 0 -> 55810 bytes .../TestResults/coverage.cobertura.xml | 2434 +++++++++++++++++ ...hangeRateUpdater.Abstraction_Currency.html | 231 ++ ...eRateUpdater.Abstraction_ExchangeRate.html | 238 ++ ...hangeRateUpdater.Api_CompilerServices.html | 167 ++ ...dater.Api_ExceptionHandlingMiddleware.html | 247 ++ ...ateUpdater.Api_ExchangeRateController.html | 206 ++ .../ExchangeRateUpdater.Api_Generated.html | 211 ++ .../ExchangeRateUpdater.Api_Program.html | 228 ++ ...lient_CnbExchangeRateMapperExtensions.html | 228 ++ ...CbnApiClient_ExchangeRateCnbApiClient.html | 243 ++ ...tion_ExchangeRateCnbServicesExtension.html | 210 ++ ...eUpdater.Service_ExchangeRateProvider.html | 234 ++ ...teUpdater.Service_ExchangeRateService.html | 224 ++ jobs/Backend/coveragereport/class.js | 210 ++ jobs/Backend/coveragereport/icon_cog.svg | 1 + jobs/Backend/coveragereport/icon_cog_dark.svg | 1 + jobs/Backend/coveragereport/icon_cube.svg | 2 + .../Backend/coveragereport/icon_cube_dark.svg | 1 + jobs/Backend/coveragereport/icon_fork.svg | 2 + .../Backend/coveragereport/icon_fork_dark.svg | 1 + .../coveragereport/icon_info-circled.svg | 2 + .../coveragereport/icon_info-circled_dark.svg | 2 + jobs/Backend/coveragereport/icon_minus.svg | 2 + .../coveragereport/icon_minus_dark.svg | 1 + jobs/Backend/coveragereport/icon_plus.svg | 2 + .../Backend/coveragereport/icon_plus_dark.svg | 1 + .../coveragereport/icon_search-minus.svg | 2 + .../coveragereport/icon_search-minus_dark.svg | 1 + .../coveragereport/icon_search-plus.svg | 2 + .../coveragereport/icon_search-plus_dark.svg | 1 + jobs/Backend/coveragereport/icon_sponsor.svg | 2 + jobs/Backend/coveragereport/icon_star.svg | 2 + .../Backend/coveragereport/icon_star_dark.svg | 2 + jobs/Backend/coveragereport/icon_up-dir.svg | 2 + .../coveragereport/icon_up-dir_active.svg | 2 + .../coveragereport/icon_up-down-dir.svg | 2 + .../coveragereport/icon_up-down-dir_dark.svg | 2 + jobs/Backend/coveragereport/icon_wrench.svg | 2 + .../coveragereport/icon_wrench_dark.svg | 1 + jobs/Backend/coveragereport/index.htm | 254 ++ jobs/Backend/coveragereport/index.html | 254 ++ jobs/Backend/coveragereport/main.js | 356 +++ jobs/Backend/coveragereport/report.css | 838 ++++++ 44 files changed, 7054 insertions(+) create mode 100644 jobs/Backend/CodeCoverage.png create mode 100644 jobs/Backend/ExchangeRateUpdater.Tests/TestResults/coverage.cobertura.xml create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.Abstraction_Currency.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.Abstraction_ExchangeRate.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.Api_CompilerServices.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.Api_ExceptionHandlingMiddleware.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.Api_ExchangeRateController.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.Api_Generated.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.Api_Program.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.CbnApiClient_CnbExchangeRateMapperExtensions.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.CbnApiClient_ExchangeRateCnbApiClient.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.DependencyInjection_ExchangeRateCnbServicesExtension.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.Service_ExchangeRateProvider.html create mode 100644 jobs/Backend/coveragereport/ExchangeRateUpdater.Service_ExchangeRateService.html create mode 100644 jobs/Backend/coveragereport/class.js create mode 100644 jobs/Backend/coveragereport/icon_cog.svg create mode 100644 jobs/Backend/coveragereport/icon_cog_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_cube.svg create mode 100644 jobs/Backend/coveragereport/icon_cube_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_fork.svg create mode 100644 jobs/Backend/coveragereport/icon_fork_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_info-circled.svg create mode 100644 jobs/Backend/coveragereport/icon_info-circled_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_minus.svg create mode 100644 jobs/Backend/coveragereport/icon_minus_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_plus.svg create mode 100644 jobs/Backend/coveragereport/icon_plus_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_search-minus.svg create mode 100644 jobs/Backend/coveragereport/icon_search-minus_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_search-plus.svg create mode 100644 jobs/Backend/coveragereport/icon_search-plus_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_sponsor.svg create mode 100644 jobs/Backend/coveragereport/icon_star.svg create mode 100644 jobs/Backend/coveragereport/icon_star_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_up-dir.svg create mode 100644 jobs/Backend/coveragereport/icon_up-dir_active.svg create mode 100644 jobs/Backend/coveragereport/icon_up-down-dir.svg create mode 100644 jobs/Backend/coveragereport/icon_up-down-dir_dark.svg create mode 100644 jobs/Backend/coveragereport/icon_wrench.svg create mode 100644 jobs/Backend/coveragereport/icon_wrench_dark.svg create mode 100644 jobs/Backend/coveragereport/index.htm create mode 100644 jobs/Backend/coveragereport/index.html create mode 100644 jobs/Backend/coveragereport/main.js create mode 100644 jobs/Backend/coveragereport/report.css diff --git a/jobs/Backend/CodeCoverage.png b/jobs/Backend/CodeCoverage.png new file mode 100644 index 0000000000000000000000000000000000000000..7e06c33386873df477f2e28f8c802ebb9af94717 GIT binary patch literal 55810 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�B}q;|V$5`kx`lvD>QTwOSf+S2eWt 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 KJkR)Q=>G@kL0bF( literal 0 HcmV?d00001 diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/TestResults/coverage.cobertura.xml b/jobs/Backend/ExchangeRateUpdater.Tests/TestResults/coverage.cobertura.xml new file mode 100644 index 0000000000..139f2b3944 --- /dev/null +++ b/jobs/Backend/ExchangeRateUpdater.Tests/TestResults/coverage.cobertura.xml @@ -0,0 +1,2434 @@ + + + + C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.Abstraction_Currency.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.Abstraction_Currency.html new file mode 100644 index 0000000000..ec2224a7d5 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.Abstraction_Currency.html @@ -0,0 +1,231 @@ + + + + + + + +ExchangeRateUpdater.Abstraction.Model.Currency - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:ExchangeRateUpdater.Abstraction.Model.Currency
Assembly:ExchangeRateUpdater.Abstraction
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.Abstraction\Model\Currency.cs
+
+
+
+
+
+
+
Line coverage
+
+
100%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:18
Uncovered lines:0
Coverable lines:18
Total lines:48
Line coverage:100%
+
+
+
+
+
Branch coverage
+
+
87%
+
+ + + + + + + + + + + + + +
Covered branches:7
Total branches:8
Branch coverage:87.5%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + + + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%66100%
get_Code()100%11100%
ToString()100%11100%
Equals(...)100%11100%
Equals(...)50%22100%
GetHashCode()100%11100%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.Abstraction\Model\Currency.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1namespace ExchangeRateUpdater.Abstraction.Model
 2{
 3    public class Currency
 4    {
 5        /// <summary>
 6        /// Initializes a new instance of the Currency class using the specified currency code.
 7        /// </summary>
 8        /// <param name="code">The currency code to represent. This value must be a non-empty string consisting of exact
 9        /// characters. The code is trimmed and converted to uppercase.</param>
 10        /// <exception cref="ArgumentException">Thrown if the specified <paramref name="code"/> is null, empty, or consi
 11        /// <exception cref="ArgumentException">Thrown if the specified <paramref name="code"/> does not have exactly th
 12        /// <exception cref="ArgumentException">Thrown if the specified <paramref name="code"/> contains non-letter char
 8313        public Currency(string code)
 8314        {
 8315            if (string.IsNullOrWhiteSpace(code))
 516                throw new ArgumentException("Currency code must not be empty.", nameof(code));
 17
 7818            code = code.Trim();
 7819            if (code.Length != 3)
 520                throw new ArgumentException("Currency code must be exactly three characters long.", nameof(code));
 21
 7322            if (!code.All(char.IsLetter))
 123                throw new ArgumentException("Currency code must consist of letters only.", nameof(code));
 24
 7225            Code = code.Trim().ToUpperInvariant();
 7226        }
 27
 28        /// <summary>
 29        /// Three-letter ISO 4217 code of the currency.
 30        /// </summary>
 8331        public string Code { get; }
 32
 33        public override string ToString()
 534        {
 535            return Code;
 536        }
 37
 38        // Compare using OrdinalIgnoreCase so the comparison is
 39        // culture-invariant and consistent across environments
 40        // (e.g. Turkish dotted/dotless i behaviour).
 1241        public bool Equals(Currency other) => string.Equals(Code, other.Code, StringComparison.OrdinalIgnoreCase);
 42
 1243        public override bool Equals(object? obj) => obj is Currency other && Equals(other);
 44
 45        // Use the same comparer for hash code to respect equality contract.
 3046        public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Code);
 47    }
 48}
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.Abstraction_ExchangeRate.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.Abstraction_ExchangeRate.html new file mode 100644 index 0000000000..ea4cc9a204 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.Abstraction_ExchangeRate.html @@ -0,0 +1,238 @@ + + + + + + + +ExchangeRateUpdater.Abstraction.Model.ExchangeRate - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:ExchangeRateUpdater.Abstraction.Model.ExchangeRate
Assembly:ExchangeRateUpdater.Abstraction
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.Abstraction\Model\ExchangeRate.cs
+
+
+
+
+
+
+
Line coverage
+
+
100%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:16
Uncovered lines:0
Coverable lines:16
Total lines:55
Line coverage:100%
+
+
+
+
+
Branch coverage
+
+
100%
+
+ + + + + + + + + + + + + +
Covered branches:2
Total branches:2
Branch coverage:100%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + + + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%22100%
get_SourceCurrency()100%11100%
get_TargetCurrency()100%11100%
get_Value()100%11100%
get_ValidFor()100%11100%
ToString()100%11100%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.Abstraction\Model\ExchangeRate.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1namespace ExchangeRateUpdater.Abstraction.Model
 2{
 3    /// <summary>
 4    /// Represents an exchange rate between two currencies, providing the source currency, target currency, and the
 5    /// exchange rate value.
 6    /// </summary>
 7    public class ExchangeRate
 8    {
 9
 10        /// <summary>
 11        /// Initializes a new instance of the ExchangeRate class using the specified source and target currencies and
 12        /// the exchange rate value.
 13        /// </summary>
 14        /// <param name="sourceCurrency">The currency from which the exchange rate is calculated.</param>
 15        /// <param name="targetCurrency">The currency to which the exchange rate is applied.</param>
 16        /// <param name="value">The exchange rate value. Must be greater than zero.</param>
 17        /// <exception cref="ArgumentOutOfRangeException">Thrown when the value parameter is less than or equal to zero.
 3218        public ExchangeRate(Currency sourceCurrency, Currency targetCurrency, decimal value, DateTime validFor)
 3219        {
 3220            if (value <= 0)
 221                throw new ArgumentOutOfRangeException(nameof(value), "Exchange rate value must be greater than zero.");
 22
 3023            SourceCurrency = sourceCurrency;
 3024            TargetCurrency = targetCurrency;
 3025            Value = value;
 3026            ValidFor = validFor;
 3027        }
 28
 29        /// <summary>
 30        /// Gets the currency used as the source for conversion.
 31        /// </summary>
 4032        public Currency SourceCurrency { get; }
 33
 34        /// <summary>
 35        /// Gets the currency to which the transaction amount will be converted.
 36        /// </summary>
 937        public Currency TargetCurrency { get; }
 38
 39        /// <summary>
 40        /// Gets the current exchange rate value represented by this instance.
 41        /// How much of the target currency is equivalent to one unit of the source currency.
 42        /// </summary>
 943        public decimal Value { get; }
 44
 45        /// <summary>
 46        /// Gets the date and time until which the current entity is valid.
 47        /// </summary>
 948        public DateTime ValidFor { get; }
 49
 50        public override string ToString()
 251        {
 252            return $"{SourceCurrency}/{TargetCurrency}={Value} (valid for {ValidFor})";
 253        }
 54    }
 55}
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_CompilerServices.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_CompilerServices.html new file mode 100644 index 0000000000..12179610c7 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_CompilerServices.html @@ -0,0 +1,167 @@ + + + + + + + +System.Runtime.CompilerServices - Coverage Report + +
+

< Summary

+ +
+
+
Line coverage
+
+
0%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:0
Uncovered lines:3
Coverable lines:3
Total lines:23
Line coverage:0%
+
+
+
+
+
Branch coverage
+
+
N/A
+
+ + + + + + + + + + + + + +
Covered branches:0
Total branches:0
Branch coverage:N/A
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\obj\Debug\net10.0\Microsoft.AspNetCore.OpenApi.SourceGenerators\Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator\OpenApiXmlCommentSupport.generated.cs

+

File 'C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\obj\Debug\net10.0\Microsoft.AspNetCore.OpenApi.SourceGenerators\Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator\OpenApiXmlCommentSupport.generated.cs' does not exist (any more).

+
+
+
+

Methods/Properties

+.ctor(System.Int32,System.String)
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_ExceptionHandlingMiddleware.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_ExceptionHandlingMiddleware.html new file mode 100644 index 0000000000..4876303ea1 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_ExceptionHandlingMiddleware.html @@ -0,0 +1,247 @@ + + + + + + + +ExchangeRateUpdater.Api.Middleware.ExceptionHandlingMiddleware - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:ExchangeRateUpdater.Api.Middleware.ExceptionHandlingMiddleware
Assembly:ExchangeRateUpdater.Api
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\Middleware\ExceptionHandlingMiddleware.cs
+
+
+
+
+
+
+
Line coverage
+
+
40%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:12
Uncovered lines:18
Coverable lines:30
Total lines:70
Line coverage:40%
+
+
+
+
+
Branch coverage
+
+
0%
+
+ + + + + + + + + + + + + +
Covered branches:0
Total branches:4
Branch coverage:0%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
InvokeAsync()100%1155.55%
HandleExceptionAsync()0%2040%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\Middleware\ExceptionHandlingMiddleware.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using System.Net;
 2using System.Text.Json;
 3using Microsoft.AspNetCore.Mvc;
 4
 5namespace ExchangeRateUpdater.Api.Middleware
 6{
 7    /// <summary>
 8    /// Middleware that handles exceptions thrown during the processing of HTTP requests, logging the error and returnin
 9    /// standardized JSON response.
 10    /// </summary>
 11    /// <param name="next">The delegate that represents the next middleware in the request processing pipeline.</param>
 12    /// <param name="logger">The logger used to log unhandled exceptions that occur during request processing.</param>
 13    /// <param name="env">The web host environment that provides information about the hosting environment, such as whet
 14    /// in development mode.</param>
 315    public class ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger, IWebHost
 16    {
 317        private readonly Dictionary<Type, HttpStatusCode> exceptionStatusCodes = new()
 318        {
 319            { typeof(ArgumentException), HttpStatusCode.BadRequest },
 320            { typeof(ArgumentNullException), HttpStatusCode.BadRequest },
 321            { typeof(InvalidOperationException), HttpStatusCode.InternalServerError  }
 322        };
 23
 24        /// <summary>
 25        /// Invokes the next middleware in the HTTP request pipeline asynchronously and handles any exceptions that
 26        /// occur during execution.
 27        /// </summary>
 28        /// <param name="context">The HttpContext for the current request, which encapsulates all HTTP-specific informat
 29        /// individual HTTP request.</param>
 30        /// <returns>A task that represents the asynchronous operation of invoking the next middleware and handling exce
 31        public async Task InvokeAsync(HttpContext context)
 432        {
 33            try
 434            {
 435                await next(context);
 436            }
 037            catch (Exception ex)
 038            {
 039                await HandleExceptionAsync(context, ex);
 040            }
 441        }
 42
 43        /// <summary>
 44        /// Handles exceptions that occur during the processing of an HTTP request by logging the error and generating a
 45        /// standardized JSON error response.
 46        /// </summary>
 47        /// <param name="context">The HttpContext for the current HTTP request, used to set the response status code and
 48        /// response.</param>
 49        /// <param name="ex">The exception that was thrown during request processing.</param>
 50        /// <returns>A task that represents the asynchronous operation.</returns>
 51        private async Task HandleExceptionAsync(HttpContext context, Exception ex)
 052        {
 053            logger.LogError(ex, "Unhandled exception");
 54
 055            context.Response.ContentType = "application/json";
 056            var status = (int)exceptionStatusCodes.GetValueOrDefault(ex.GetType(), HttpStatusCode.InternalServerError);
 057            context.Response.StatusCode = status;
 58
 059            var problem = new ProblemDetails
 060            {
 061                Status = status,
 062                Title = status == 500 ? "An internal error occurred." : "A request error occurred.",
 063                Detail = env.IsDevelopment() ? ex.ToString() : null
 064            };
 65
 066            var json = JsonSerializer.Serialize(problem);
 067            await context.Response.WriteAsync(json);
 068        }
 69    }
 70}
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_ExchangeRateController.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_ExchangeRateController.html new file mode 100644 index 0000000000..95ecd194f6 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_ExchangeRateController.html @@ -0,0 +1,206 @@ + + + + + + + +ExchangeRateUpdater.Api.Controllers.ExchangeRateController - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:ExchangeRateUpdater.Api.Controllers.ExchangeRateController
Assembly:ExchangeRateUpdater.Api
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\Controllers\ExchangeRateController.cs
+
+
+
+
+
+
+
Line coverage
+
+
100%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:13
Uncovered lines:0
Coverable lines:13
Total lines:31
Line coverage:100%
+
+
+
+
+
Branch coverage
+
+
100%
+
+ + + + + + + + + + + + + +
Covered branches:2
Total branches:2
Branch coverage:100%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetExchangeRates()100%22100%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\Controllers\ExchangeRateController.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using ExchangeRateUpdater.Abstraction.Interfaces;
 2using ExchangeRateUpdater.Abstraction.Model;
 3using Microsoft.AspNetCore.Mvc;
 4
 5namespace ExchangeRateUpdater.Api.Controllers
 6{
 7    [ApiController]
 8    [Route("[controller]")]
 69    public class ExchangeRateController(
 610        IExchangeRateService exchangeRateService,
 611        ILogger<ExchangeRateController> logger) : ControllerBase
 12    {
 13        [HttpGet]
 14        public async Task<ActionResult<IEnumerable<ExchangeRate>>> GetExchangeRates([FromQuery] string currencyCodes)
 615        {
 616            logger.LogInformation("Received request to fetch exchange rates for currencies: {CurrencyCodes}", currencyCo
 17
 618            string[] codes = currencyCodes.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEnt
 19
 620            IEnumerable<ExchangeRate> exchangeRates = await exchangeRateService.GetExchangeRatesFromStringList(codes);
 621            if (exchangeRates.ToList().Count == 0)
 222            {
 223                logger.LogWarning("No exchange rates found for the provided currency codes: {CurrencyCodes}", currencyCo
 224                return BadRequest("No exchange rates found for the provided currency codes.");
 25            }
 26
 427            return Ok(exchangeRates);
 28
 629        }
 30    }
 31}
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_Generated.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_Generated.html new file mode 100644 index 0000000000..b68cbeacd8 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_Generated.html @@ -0,0 +1,211 @@ + + + + + + + +Microsoft.AspNetCore.OpenApi.Generated - Coverage Report + +
+

< Summary

+ +
+
+
Line coverage
+
+
0%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:0
Uncovered lines:374
Coverable lines:374
Total lines:592
Line coverage:0%
+
+
+
+
+
Branch coverage
+
+
0%
+
+ + + + + + + + + + + + + +
Covered branches:0
Total branches:206
Branch coverage:0%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Summary()100%210%
get_Description()100%210%
get_Remarks()100%210%
get_Returns()100%210%
get_Value()100%210%
get_Deprecated()100%210%
get_Examples()100%210%
get_Parameters()100%210%
get_Responses()100%210%
get_Name()100%210%
get_Code()100%210%
get_Cache()0%620%
GenerateCacheEntries()100%210%
CreateDocumentationId(...)0%620%
CreateDocumentationId(...)0%110100%
CreateDocumentationId(...)0%2040%
CreateDocumentationId(...)0%342180%
GetTypeDocId(...)0%812280%
NormalizeDocId(...)0%4260%
TransformAsync(...)0%8930940%
UnwrapOpenApiParameter(...)0%4260%
TransformAsync(...)0%1190340%
Parse(...)0%620%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\obj\Debug\net10.0\Microsoft.AspNetCore.OpenApi.SourceGenerators\Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator\OpenApiXmlCommentSupport.generated.cs

+

File 'C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\obj\Debug\net10.0\Microsoft.AspNetCore.OpenApi.SourceGenerators\Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator\OpenApiXmlCommentSupport.generated.cs' does not exist (any more).

+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_Program.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_Program.html new file mode 100644 index 0000000000..c37e5ed8a1 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.Api_Program.html @@ -0,0 +1,228 @@ + + + + + + + +Program - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:Program
Assembly:ExchangeRateUpdater.Api
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\Program.cs
+
+
+
+
+
+
+
Line coverage
+
+
86%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:26
Uncovered lines:4
Coverable lines:30
Total lines:55
Line coverage:86.6%
+
+
+
+
+
Branch coverage
+
+
100%
+
+ + + + + + + + + + + + + +
Covered branches:2
Total branches:2
Branch coverage:100%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
<Main>$(...)100%2286.66%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.API\Program.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using ExchangeRateUpdater.Api.Middleware;
 2using ExchangeRateUpdater.DependencyInjection;
 3using Serilog;
 4
 5
 36var builder = WebApplication.CreateBuilder(args);
 7
 8// Configure Serilog from configuration (appsettings.json)
 39Log.Logger = new LoggerConfiguration()
 310    .ReadFrom.Configuration(builder.Configuration)
 311    .Enrich.FromLogContext()
 312    .CreateLogger();
 13
 314builder.Host.UseSerilog();
 15
 16try
 317{
 318    Log.Information("Starting web host");
 19
 20    // Add services to the container.
 321    builder.Services.AddControllers();
 322    builder.Services.AddExchangeRateServices();
 23
 24    // Add Swagger services
 325    builder.Services.AddEndpointsApiExplorer();
 326    builder.Services.AddSwaggerGen();
 27
 28
 329    var app = builder.Build();
 30
 331    if (app.Environment.IsDevelopment())
 332    {
 33        // Enable Swagger UI in development
 334        app.UseSwagger();
 335        app.UseSwaggerUI();
 336    }
 37
 338    app.UseMiddleware<ExceptionHandlingMiddleware>();
 39
 340    app.UseHttpsRedirection();
 41
 342    app.MapControllers();
 43
 344    app.Run();
 345}
 046catch (Exception ex)
 047{
 48    // Catch startup exceptions
 049    Log.Fatal(ex, "Host terminated unexpectedly");
 050    throw;
 51}
 52finally
 353{
 354    Log.CloseAndFlush();
 355}
+
+
+
+
+

Methods/Properties

+<Main>$(System.String[])
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.CbnApiClient_CnbExchangeRateMapperExtensions.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.CbnApiClient_CnbExchangeRateMapperExtensions.html new file mode 100644 index 0000000000..eca99233fa --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.CbnApiClient_CnbExchangeRateMapperExtensions.html @@ -0,0 +1,228 @@ + + + + + + + +ExchangeRateUpdater.CbnApiClient.Mapper.CnbExchangeRateMapperExtensions - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:ExchangeRateUpdater.CbnApiClient.Mapper.CnbExchangeRateMapperExtensions
Assembly:ExchangeRateUpdater.CbnApiClient
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.CbnApiClient\Mapper\CnbExchangeRateMapperExtensions.cs
+
+
+
+
+
+
+
Line coverage
+
+
100%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:15
Uncovered lines:0
Coverable lines:15
Total lines:53
Line coverage:100%
+
+
+
+
+
Branch coverage
+
+
100%
+
+ + + + + + + + + + + + + +
Covered branches:2
Total branches:2
Branch coverage:100%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ToDomain(...)100%22100%
ToDomain(...)100%11100%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.CbnApiClient\Mapper\CnbExchangeRateMapperExtensions.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using ExchangeRateUpdater.CbnApiClient.Dto;
 2using ExchangeRateUpdater.Abstraction.Model;
 3
 4namespace ExchangeRateUpdater.CbnApiClient.Mapper
 5{
 6
 7    /// <summary>
 8    /// Extension methods to map CNB API DTOs to domain models.
 9    /// </summary>
 10    public static class CnbExchangeRateMapperExtensions
 11    {
 12        private const string TARGET_CURRENCY_CODE = "CZK";
 13        /// <summary>
 14        /// Maps a single <see cref="ExchangeRateEntry"/> to a domain <see cref="ExchangeRate"/>.
 15        /// By convention the CNB API provides rates as an amount of CZK for a given <see cref="ExchangeRateEntry.Amount
 16        /// units of the foreign currency. This method maps the entry to an ExchangeRate where the source currency
 17        /// is the foreign currency and the target currency is CZK. The resulting value is the rate for one unit of
 18        /// the source currency (Rate / Amount).
 19        /// </summary>
 20        /// <param name="entry">The DTO entry to map.</param>
 21        /// <returns>The mapped <see cref="ExchangeRate"/>.</returns>
 22        public static ExchangeRate ToDomain(this ExchangeRateEntry entry)
 2423        {
 2424            ArgumentNullException.ThrowIfNull(entry);
 25
 26            // Avoid division by zero and ensure the amount is positive as expected by the CNB API.
 2327            if (entry.Amount <= 0)
 128                throw new ArgumentException("Entry amount must be greater than zero.", nameof(entry));
 29
 30            // CNB returns the amount of CZK for 'Amount' units of the foreign currency.
 31            // Normalize to a per-1-unit rate.
 2232            var valuePerUnit = entry.Rate / entry.Amount;
 33
 2234            var source = new Currency(entry.CurrencyCode);
 2235            var target = new Currency(TARGET_CURRENCY_CODE);
 36
 2237            return new ExchangeRate(source, target, valuePerUnit, entry.ValidFor);
 2238        }
 39
 40        /// <summary>
 41        /// Maps a sequence of <see cref="ExchangeRateEntry"/> to <see cref="IEnumerable{ExchangeRate}"/>.
 42        /// Null entries are ignored.
 43        /// </summary>
 44        public static IEnumerable<ExchangeRate> ToDomain(this IEnumerable<ExchangeRateEntry> entries)
 645        {
 646            ArgumentNullException.ThrowIfNull(entries);
 47
 548            return entries
 2149                .Where(e => e is not null)
 2650                .Select(e => e.ToDomain());
 551        }
 52    }
 53}
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.CbnApiClient_ExchangeRateCnbApiClient.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.CbnApiClient_ExchangeRateCnbApiClient.html new file mode 100644 index 0000000000..2becb63862 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.CbnApiClient_ExchangeRateCnbApiClient.html @@ -0,0 +1,243 @@ + + + + + + + +ExchangeRateUpdater.CbnApiClient.Implementation.ExchangeRateCnbApiClient - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:ExchangeRateUpdater.CbnApiClient.Implementation.ExchangeRateCnbApiClient
Assembly:ExchangeRateUpdater.CbnApiClient
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.CbnApiClient\Implementation\ExchangeRateCnbApiClient.cs
+
+
+
+
+
+
+
Line coverage
+
+
96%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:31
Uncovered lines:1
Coverable lines:32
Total lines:68
Line coverage:96.8%
+
+
+
+
+
Branch coverage
+
+
80%
+
+ + + + + + + + + + + + + +
Covered branches:8
Total branches:10
Branch coverage:80%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%22100%
GetExchangeRatesAsync()87.5%8896.42%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.CbnApiClient\Implementation\ExchangeRateCnbApiClient.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using ExchangeRateUpdater.Abstraction.Interfaces;
 2using ExchangeRateUpdater.Abstraction.Model;
 3using ExchangeRateUpdater.CbnApiClient.Dto;
 4using ExchangeRateUpdater.CbnApiClient.Mapper;
 5using Microsoft.Extensions.Configuration;
 6using Microsoft.Extensions.Logging;
 7using System.Text.Json;
 8
 9namespace ExchangeRateUpdater.CbnApiClient.Implementation
 10{
 911    public class ExchangeRateCnbApiClient(IHttpClientFactory httpClientFactory, IConfiguration config, ILogger<ExchangeR
 12    {
 913        private readonly string CnbApiUrl = config.GetValue<string>("CbnApiUrl") ?? throw new InvalidOperationException(
 14        // Supports EN and CZ, CZ is the default if not specified.
 915        private readonly string CnbLangToRequest = "EN";
 916        private readonly string CbnApiClientName = "CbnApi";
 17
 18        /// <summary>
 19        /// Fetches exchange rates from the CBN API for the specified date. If no date is provided, it fetches today's r
 20        /// </summary>
 21        /// <param name="validDate">The date for which to fetch exchange rates. If null, today's rates will be fetched.<
 22        /// <returns>A collection of <see cref="ExchangeRate"/> objects representing the exchange rates for the specifie
 23        /// <exception cref="HttpRequestException">Thrown when the HTTP request to the CBN API fails.</exception>
 24        /// <exception cref="InvalidOperationException">Thrown when the response from the CBN API is empty or cannot be 
 25        public async Task<IEnumerable<ExchangeRate>> GetExchangeRatesAsync(DateTime? validDate = null)
 726        {
 27            // prepare the url, add today date as yyyy-MM-dd if specified and lang to "EN"
 728            string urlWithParams = $"{CnbApiUrl}?lang={CnbLangToRequest}";
 29            // if no date is specified, the CBN API will return today as default
 730            if (validDate.HasValue)
 131            {
 132                urlWithParams += $"&date={validDate.Value:yyyy-MM-dd}";
 133            }
 34            // make the request to the CBN API using the named client so configured handlers/policies are applied
 735            HttpClient httpClient = httpClientFactory.CreateClient(CbnApiClientName);
 736            using HttpResponseMessage response = await httpClient.GetAsync(urlWithParams);
 737            if (!response.IsSuccessStatusCode)
 138            {
 139                throw new HttpRequestException($"Failed to fetch exchange rates from CBN API. Status code: {response.Sta
 40            }
 41
 42            // check content is not empty
 643            string content = await response.Content.ReadAsStringAsync();
 644            if (string.IsNullOrWhiteSpace(content))
 145            {
 146                string message = "Received empty response from CBN API.";
 147                logger.LogError(message);
 148                throw new InvalidOperationException(message);
 49            }
 50
 51            // deserialize the response. JsonSerializer.Deserialize may throw JsonException for invalid JSON;
 52            ExchangeRatesResponse? dtoRates;
 53            try
 554            {
 555                dtoRates = JsonSerializer.Deserialize<ExchangeRatesResponse>(content);
 456            }
 157            catch (JsonException jex)
 158            {
 159                logger.LogError(jex, "Failed to deserialize CBN API response JSON.");
 160                throw new InvalidOperationException("Failed to deserialize CBN API response.", jex);
 61            }
 62
 463            if (dtoRates is null)
 064                throw new InvalidOperationException("Failed to deserialize CBN API response.");
 465            return dtoRates.Rates.ToDomain();
 466        }
 67    }
 68}
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.DependencyInjection_ExchangeRateCnbServicesExtension.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.DependencyInjection_ExchangeRateCnbServicesExtension.html new file mode 100644 index 0000000000..5ea6112149 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.DependencyInjection_ExchangeRateCnbServicesExtension.html @@ -0,0 +1,210 @@ + + + + + + + +ExchangeRateUpdater.DependencyInjection.ExchangeRateCnbServicesExtension - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:ExchangeRateUpdater.DependencyInjection.ExchangeRateCnbServicesExtension
Assembly:ExchangeRateUpdater.DependencyInjection
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.DependencyInjection\ExchangeRateCnbServicesExtension.cs
+
+
+
+
+
+
+
Line coverage
+
+
91%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:11
Uncovered lines:1
Coverable lines:12
Total lines:37
Line coverage:91.6%
+
+
+
+
+
Branch coverage
+
+
N/A
+
+ + + + + + + + + + + + + +
Covered branches:0
Total branches:0
Branch coverage:N/A
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
AddExchangeRateServices(...)100%1191.66%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.DependencyInjection\ExchangeRateCnbServicesExtension.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using ExchangeRateUpdater.Abstraction.Interfaces;
 2using ExchangeRateUpdater.CbnApiClient.Implementation;
 3using ExchangeRateUpdater.Service;
 4using Microsoft.Extensions.DependencyInjection;
 5using Polly;
 6using Polly.Extensions.Http;
 7
 8namespace ExchangeRateUpdater.DependencyInjection
 9{
 10    /// <summary>
 11    /// Provides extension methods for registering exchange rate client services with the dependency injection
 12    /// container.
 13    /// </summary>
 14    /// <remarks>This static class is intended to be used in application startup to ensure that the
 15    /// IExchangeRateClient implementation is available for dependency injection. It simplifies the configuration of
 16    /// exchange rate services by encapsulating the registration logic.</remarks>
 17    public static class ExchangeRateCnbServicesExtension
 18    {
 19        public static IServiceCollection AddExchangeRateServices(this IServiceCollection services)
 320        {
 321            services.AddMemoryCache();
 322            services.AddScoped<IExchangeRateProvider, ExchangeRateProvider>();
 323            services.AddScoped<IExchangeRateService, ExchangeRateService>();
 324            services.AddScoped<IExchangeRateClient, ExchangeRateCnbApiClient>();
 25
 26            // Configure a named HttpClient for the CNB API with Polly-based retry policy.
 27            // This will be available as httpClientFactory.CreateClient("CnbApi").
 328            services.AddHttpClient("CnbApi")
 329                .AddPolicyHandler(HttpPolicyExtensions
 330                    .HandleTransientHttpError()
 031                    .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
 332                );
 33
 334            return services;
 335        }
 36    }
 37}
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.Service_ExchangeRateProvider.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.Service_ExchangeRateProvider.html new file mode 100644 index 0000000000..c2e3a16f97 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.Service_ExchangeRateProvider.html @@ -0,0 +1,234 @@ + + + + + + + +ExchangeRateUpdater.Service.ExchangeRateProvider - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:ExchangeRateUpdater.Service.ExchangeRateProvider
Assembly:ExchangeRateUpdater.Service
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.Service\ExchangeRateProvider.cs
+
+
+
+
+
+
+
Line coverage
+
+
89%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:25
Uncovered lines:3
Coverable lines:28
Total lines:59
Line coverage:89.2%
+
+
+
+
+
Branch coverage
+
+
62%
+
+ + + + + + + + + + + + + +
Covered branches:5
Total branches:8
Branch coverage:62.5%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetExchangeRatesAsync()62.5%8888.46%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.Service\ExchangeRateProvider.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using ExchangeRateUpdater.Abstraction.Interfaces;
 2using ExchangeRateUpdater.Abstraction.Model;
 3using Microsoft.Extensions.Caching.Memory;
 4using Microsoft.Extensions.Logging;
 5
 6namespace ExchangeRateUpdater.Service
 7{
 8    /// <summary>
 9    /// Provides methods for retrieving exchange rates for specified currencies from an external source.
 10    /// </summary>
 11    /// <param name="exchangeRateClient">The client used to obtain exchange rate data from an external provider.</param>
 12    /// <param name="logger">The logger used to record informational messages and errors related to exchange rate retrie
 613    public class ExchangeRateProvider(IExchangeRateClient exchangeRateClient, ILogger<ExchangeRateProvider> logger, IMem
 14    {
 15        private const string CacheKey = "CnbRates";
 16
 17        public async Task<IEnumerable<ExchangeRate>> GetExchangeRatesAsync(IEnumerable<Currency> currencies)
 618        {
 619            ArgumentNullException.ThrowIfNull(currencies);
 20
 21            // Try get from cache first
 522            if (!cache.TryGetValue<IEnumerable<ExchangeRate>>(CacheKey, out var cachedRates))
 323            {
 324                logger.LogInformation("Cache miss for CNB rates, fetching from upstream");
 325                var fetched = await exchangeRateClient.GetExchangeRatesAsync();
 26
 27                // Determine cache expiration: CNB publishes new rates around 14:30 UTC on working days.
 28                // Set 30 minutes margin to ensure we get the new rates after publication.
 329                var now = DateTime.UtcNow;
 330                var nextPublication = new DateTime(now.Year, now.Month, now.Day, 15, 0, 0, DateTimeKind.Utc);
 331                if (now >= nextPublication)
 032                {
 33                    // already past today's publication time, schedule for next day
 034                    nextPublication = nextPublication.AddDays(1);
 035                }
 36
 337                var absoluteExpirationRelative = nextPublication - now;
 38                // Guard: ensure a minimum cache duration and reasonable maximum (24h)
 39                // Cache will refresh even on non-working days, but that's acceptable because 1 unnecessary request a da
 340                if (absoluteExpirationRelative < TimeSpan.FromMinutes(1)) absoluteExpirationRelative = TimeSpan.FromMinu
 341                if (absoluteExpirationRelative > TimeSpan.FromHours(24)) absoluteExpirationRelative = TimeSpan.FromHours
 42
 343                var options = new MemoryCacheEntryOptions()
 344                    .SetAbsoluteExpiration(absoluteExpirationRelative);
 45
 346                cache.Set(CacheKey, fetched, options);
 347                cachedRates = fetched;
 348            }
 49            else
 250            {
 251                logger.LogDebug("Cache hit for CNB rates");
 252            }
 53
 554            var currencySet = currencies.ToHashSet();
 2755            var filteredRates = cachedRates.Where(rate => currencySet.Contains(rate.SourceCurrency));
 556            return filteredRates;
 557        }
 58    }
 59}
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/ExchangeRateUpdater.Service_ExchangeRateService.html b/jobs/Backend/coveragereport/ExchangeRateUpdater.Service_ExchangeRateService.html new file mode 100644 index 0000000000..9d60a79cc5 --- /dev/null +++ b/jobs/Backend/coveragereport/ExchangeRateUpdater.Service_ExchangeRateService.html @@ -0,0 +1,224 @@ + + + + + + + +ExchangeRateUpdater.Service.ExchangeRateService - Coverage Report + +
+

< Summary

+
+
+
Information
+
+
+ + + + + + + + + + + + + +
Class:ExchangeRateUpdater.Service.ExchangeRateService
Assembly:ExchangeRateUpdater.Service
File(s):C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.Service\ExchangeRateService.cs
+
+
+
+
+
+
+
Line coverage
+
+
100%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:21
Uncovered lines:0
Coverable lines:21
Total lines:49
Line coverage:100%
+
+
+
+
+
Branch coverage
+
+
100%
+
+ + + + + + + + + + + + + +
Covered branches:4
Total branches:4
Branch coverage:100%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Metrics

+
+ +++++++ + + + + + +
MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetExchangeRatesFromStringList()100%44100%
+
+

File(s)

+

C:\Users\alvar\Documents\MEWS-TASK\developers\jobs\Backend\ExchangeRateUpdater.Service\ExchangeRateService.cs

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using ExchangeRateUpdater.Abstraction.Interfaces;
 2using ExchangeRateUpdater.Abstraction.Model;
 3using Microsoft.Extensions.Logging;
 4
 5namespace ExchangeRateUpdater.Service
 6{
 7    /// <summary>
 8    /// Provides functionality to retrieve exchange rates for specified currency codes.
 9    /// </summary>
 10    /// <param name="exchangeRateProvider">The provider used to fetch exchange rate data.</param>
 11    /// <param name="logger">The logger instance used to record informational and error messages for this service.</para
 712    public class ExchangeRateService(IExchangeRateProvider exchangeRateProvider, ILogger<ExchangeRateService> logger) : 
 13    {
 14        /// <summary>
 15        /// Retrieves exchange rates for the specified collection of currency codes.
 16        /// </summary>
 17        /// <param name="currencyCodes">An enumerable collection of currency code strings to retrieve exchange rates for
 18        /// a valid currency code.</param>
 19        /// <returns>A task that represents the asynchronous operation. The task result contains a collection of exchang
 20        /// corresponding to the valid currency codes provided. Returns an empty collection if no valid codes are
 21        /// supplied.</returns>
 22        public async Task<IEnumerable<ExchangeRate>> GetExchangeRatesFromStringList(IEnumerable<string> currencyCodes)
 723        {
 724            List<Currency> currencyList = new();
 4325            foreach (var currencyCode in currencyCodes)
 1126            {
 27                try
 1128                {
 1129                    var currency = new Currency(currencyCode);
 630                    currencyList.Add(currency);
 631                }
 532                catch (ArgumentException ex)
 533                {
 534                    logger.LogWarning($"Invalid currency {currencyCode}: {ex.Message}");
 535                    continue;
 36                }
 637            }
 38
 739            if (currencyList.Count == 0)
 240            {
 241                logger.LogWarning("No valid currency codes provided.");
 242                return [];
 43            }
 44
 545            IEnumerable<ExchangeRate> exchangeRates = await exchangeRateProvider.GetExchangeRatesAsync(currencyList);
 546            return exchangeRates;
 747        }
 48    }
 49}
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/class.js b/jobs/Backend/coveragereport/class.js new file mode 100644 index 0000000000..3976a9714d --- /dev/null +++ b/jobs/Backend/coveragereport/class.js @@ -0,0 +1,210 @@ +/* Chartist.js 0.11.4 + * Copyright © 2019 Gion Kunz + * Free to use under either the WTFPL license or the MIT license. + * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL + * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT + */ + +!function (e, t) { "function" == typeof define && define.amd ? define("Chartist", [], (function () { return e.Chartist = t() })) : "object" == typeof module && module.exports ? module.exports = t() : e.Chartist = t() }(this, (function () { var e = { version: "0.11.4" }; return function (e, t) { "use strict"; var i = e.window, n = e.document; t.namespaces = { svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns/", xhtml: "http://www.w3.org/1999/xhtml", xlink: "http://www.w3.org/1999/xlink", ct: "http://gionkunz.github.com/chartist-js/ct" }, t.noop = function (e) { return e }, t.alphaNumerate = function (e) { return String.fromCharCode(97 + e % 26) }, t.extend = function (e) { var i, n, s, r; for (e = e || {}, i = 1; i < arguments.length; i++)for (var a in n = arguments[i], r = Object.getPrototypeOf(e), n) "__proto__" === a || "constructor" === a || null !== r && a in r || (s = n[a], e[a] = "object" != typeof s || null === s || s instanceof Array ? s : t.extend(e[a], s)); return e }, t.replaceAll = function (e, t, i) { return e.replace(new RegExp(t, "g"), i) }, t.ensureUnit = function (e, t) { return "number" == typeof e && (e += t), e }, t.quantity = function (e) { if ("string" == typeof e) { var t = /^(\d+)\s*(.*)$/g.exec(e); return { value: +t[1], unit: t[2] || void 0 } } return { value: e } }, t.querySelector = function (e) { return e instanceof Node ? e : n.querySelector(e) }, t.times = function (e) { return Array.apply(null, new Array(e)) }, t.sum = function (e, t) { return e + (t || 0) }, t.mapMultiply = function (e) { return function (t) { return t * e } }, t.mapAdd = function (e) { return function (t) { return t + e } }, t.serialMap = function (e, i) { var n = [], s = Math.max.apply(null, e.map((function (e) { return e.length }))); return t.times(s).forEach((function (t, s) { var r = e.map((function (e) { return e[s] })); n[s] = i.apply(null, r) })), n }, t.roundWithPrecision = function (e, i) { var n = Math.pow(10, i || t.precision); return Math.round(e * n) / n }, t.precision = 8, t.escapingMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, t.serialize = function (e) { return null == e ? e : ("number" == typeof e ? e = "" + e : "object" == typeof e && (e = JSON.stringify({ data: e })), Object.keys(t.escapingMap).reduce((function (e, i) { return t.replaceAll(e, i, t.escapingMap[i]) }), e)) }, t.deserialize = function (e) { if ("string" != typeof e) return e; e = Object.keys(t.escapingMap).reduce((function (e, i) { return t.replaceAll(e, t.escapingMap[i], i) }), e); try { e = void 0 !== (e = JSON.parse(e)).data ? e.data : e } catch (e) { } return e }, t.createSvg = function (e, i, n, s) { var r; return i = i || "100%", n = n || "100%", Array.prototype.slice.call(e.querySelectorAll("svg")).filter((function (e) { return e.getAttributeNS(t.namespaces.xmlns, "ct") })).forEach((function (t) { e.removeChild(t) })), (r = new t.Svg("svg").attr({ width: i, height: n }).addClass(s))._node.style.width = i, r._node.style.height = n, e.appendChild(r._node), r }, t.normalizeData = function (e, i, n) { var s, r = { raw: e, normalized: {} }; return r.normalized.series = t.getDataArray({ series: e.series || [] }, i, n), s = r.normalized.series.every((function (e) { return e instanceof Array })) ? Math.max.apply(null, r.normalized.series.map((function (e) { return e.length }))) : r.normalized.series.length, r.normalized.labels = (e.labels || []).slice(), Array.prototype.push.apply(r.normalized.labels, t.times(Math.max(0, s - r.normalized.labels.length)).map((function () { return "" }))), i && t.reverseData(r.normalized), r }, t.safeHasProperty = function (e, t) { return null !== e && "object" == typeof e && e.hasOwnProperty(t) }, t.isDataHoleValue = function (e) { return null == e || "number" == typeof e && isNaN(e) }, t.reverseData = function (e) { e.labels.reverse(), e.series.reverse(); for (var t = 0; t < e.series.length; t++)"object" == typeof e.series[t] && void 0 !== e.series[t].data ? e.series[t].data.reverse() : e.series[t] instanceof Array && e.series[t].reverse() }, t.getDataArray = function (e, i, n) { return e.series.map((function e(i) { if (t.safeHasProperty(i, "value")) return e(i.value); if (t.safeHasProperty(i, "data")) return e(i.data); if (i instanceof Array) return i.map(e); if (!t.isDataHoleValue(i)) { if (n) { var s = {}; return "string" == typeof n ? s[n] = t.getNumberOrUndefined(i) : s.y = t.getNumberOrUndefined(i), s.x = i.hasOwnProperty("x") ? t.getNumberOrUndefined(i.x) : s.x, s.y = i.hasOwnProperty("y") ? t.getNumberOrUndefined(i.y) : s.y, s } return t.getNumberOrUndefined(i) } })) }, t.normalizePadding = function (e, t) { return t = t || 0, "number" == typeof e ? { top: e, right: e, bottom: e, left: e } : { top: "number" == typeof e.top ? e.top : t, right: "number" == typeof e.right ? e.right : t, bottom: "number" == typeof e.bottom ? e.bottom : t, left: "number" == typeof e.left ? e.left : t } }, t.getMetaData = function (e, t) { var i = e.data ? e.data[t] : e[t]; return i ? i.meta : void 0 }, t.orderOfMagnitude = function (e) { return Math.floor(Math.log(Math.abs(e)) / Math.LN10) }, t.projectLength = function (e, t, i) { return t / i.range * e }, t.getAvailableHeight = function (e, i) { return Math.max((t.quantity(i.height).value || e.height()) - (i.chartPadding.top + i.chartPadding.bottom) - i.axisX.offset, 0) }, t.getHighLow = function (e, i, n) { var s = { high: void 0 === (i = t.extend({}, i, n ? i["axis" + n.toUpperCase()] : {})).high ? -Number.MAX_VALUE : +i.high, low: void 0 === i.low ? Number.MAX_VALUE : +i.low }, r = void 0 === i.high, a = void 0 === i.low; return (r || a) && function e(t) { if (void 0 !== t) if (t instanceof Array) for (var i = 0; i < t.length; i++)e(t[i]); else { var o = n ? +t[n] : +t; r && o > s.high && (s.high = o), a && o < s.low && (s.low = o) } }(e), (i.referenceValue || 0 === i.referenceValue) && (s.high = Math.max(i.referenceValue, s.high), s.low = Math.min(i.referenceValue, s.low)), s.high <= s.low && (0 === s.low ? s.high = 1 : s.low < 0 ? s.high = 0 : (s.high > 0 || (s.high = 1), s.low = 0)), s }, t.isNumeric = function (e) { return null !== e && isFinite(e) }, t.isFalseyButZero = function (e) { return !e && 0 !== e }, t.getNumberOrUndefined = function (e) { return t.isNumeric(e) ? +e : void 0 }, t.isMultiValue = function (e) { return "object" == typeof e && ("x" in e || "y" in e) }, t.getMultiValue = function (e, i) { return t.isMultiValue(e) ? t.getNumberOrUndefined(e[i || "y"]) : t.getNumberOrUndefined(e) }, t.rho = function (e) { if (1 === e) return e; function t(e, i) { return e % i == 0 ? i : t(i, e % i) } function i(e) { return e * e + 1 } var n, s = 2, r = 2; if (e % 2 == 0) return 2; do { s = i(s) % e, r = i(i(r)) % e, n = t(Math.abs(s - r), e) } while (1 === n); return n }, t.getBounds = function (e, i, n, s) { var r, a, o, l = 0, h = { high: i.high, low: i.low }; h.valueRange = h.high - h.low, h.oom = t.orderOfMagnitude(h.valueRange), h.step = Math.pow(10, h.oom), h.min = Math.floor(h.low / h.step) * h.step, h.max = Math.ceil(h.high / h.step) * h.step, h.range = h.max - h.min, h.numberOfSteps = Math.round(h.range / h.step); var u = t.projectLength(e, h.step, h) < n, c = s ? t.rho(h.range) : 0; if (s && t.projectLength(e, 1, h) >= n) h.step = 1; else if (s && c < h.step && t.projectLength(e, c, h) >= n) h.step = c; else for (; ;) { if (u && t.projectLength(e, h.step, h) <= n) h.step *= 2; else { if (u || !(t.projectLength(e, h.step / 2, h) >= n)) break; if (h.step /= 2, s && h.step % 1 != 0) { h.step *= 2; break } } if (l++ > 1e3) throw new Error("Exceeded maximum number of iterations while optimizing scale step!") } var d = 2221e-19; function p(e, t) { return e === (e += t) && (e *= 1 + (t > 0 ? d : -d)), e } for (h.step = Math.max(h.step, d), a = h.min, o = h.max; a + h.step <= h.low;)a = p(a, h.step); for (; o - h.step >= h.high;)o = p(o, -h.step); h.min = a, h.max = o, h.range = h.max - h.min; var f = []; for (r = h.min; r <= h.max; r = p(r, h.step)) { var m = t.roundWithPrecision(r); m !== f[f.length - 1] && f.push(m) } return h.values = f, h }, t.polarToCartesian = function (e, t, i, n) { var s = (n - 90) * Math.PI / 180; return { x: e + i * Math.cos(s), y: t + i * Math.sin(s) } }, t.createChartRect = function (e, i, n) { var s = !(!i.axisX && !i.axisY), r = s ? i.axisY.offset : 0, a = s ? i.axisX.offset : 0, o = e.width() || t.quantity(i.width).value || 0, l = e.height() || t.quantity(i.height).value || 0, h = t.normalizePadding(i.chartPadding, n); o = Math.max(o, r + h.left + h.right), l = Math.max(l, a + h.top + h.bottom); var u = { padding: h, width: function () { return this.x2 - this.x1 }, height: function () { return this.y1 - this.y2 } }; return s ? ("start" === i.axisX.position ? (u.y2 = h.top + a, u.y1 = Math.max(l - h.bottom, u.y2 + 1)) : (u.y2 = h.top, u.y1 = Math.max(l - h.bottom - a, u.y2 + 1)), "start" === i.axisY.position ? (u.x1 = h.left + r, u.x2 = Math.max(o - h.right, u.x1 + 1)) : (u.x1 = h.left, u.x2 = Math.max(o - h.right - r, u.x1 + 1))) : (u.x1 = h.left, u.x2 = Math.max(o - h.right, u.x1 + 1), u.y2 = h.top, u.y1 = Math.max(l - h.bottom, u.y2 + 1)), u }, t.createGrid = function (e, i, n, s, r, a, o, l) { var h = {}; h[n.units.pos + "1"] = e, h[n.units.pos + "2"] = e, h[n.counterUnits.pos + "1"] = s, h[n.counterUnits.pos + "2"] = s + r; var u = a.elem("line", h, o.join(" ")); l.emit("draw", t.extend({ type: "grid", axis: n, index: i, group: a, element: u }, h)) }, t.createGridBackground = function (e, t, i, n) { var s = e.elem("rect", { x: t.x1, y: t.y2, width: t.width(), height: t.height() }, i, !0); n.emit("draw", { type: "gridBackground", group: e, element: s }) }, t.createLabel = function (e, i, s, r, a, o, l, h, u, c, d) { var p, f = {}; if (f[a.units.pos] = e + l[a.units.pos], f[a.counterUnits.pos] = l[a.counterUnits.pos], f[a.units.len] = i, f[a.counterUnits.len] = Math.max(0, o - 10), c) { var m = n.createElement("span"); m.className = u.join(" "), m.setAttribute("xmlns", t.namespaces.xhtml), m.innerText = r[s], m.style[a.units.len] = Math.round(f[a.units.len]) + "px", m.style[a.counterUnits.len] = Math.round(f[a.counterUnits.len]) + "px", p = h.foreignObject(m, t.extend({ style: "overflow: visible;" }, f)) } else p = h.elem("text", f, u.join(" ")).text(r[s]); d.emit("draw", t.extend({ type: "label", axis: a, index: s, group: h, element: p, text: r[s] }, f)) }, t.getSeriesOption = function (e, t, i) { if (e.name && t.series && t.series[e.name]) { var n = t.series[e.name]; return n.hasOwnProperty(i) ? n[i] : t[i] } return t[i] }, t.optionsProvider = function (e, n, s) { var r, a, o = t.extend({}, e), l = []; function h(e) { var l = r; if (r = t.extend({}, o), n) for (a = 0; a < n.length; a++) { i.matchMedia(n[a][0]).matches && (r = t.extend(r, n[a][1])) } s && e && s.emit("optionsChanged", { previousOptions: l, currentOptions: r }) } if (!i.matchMedia) throw "window.matchMedia not found! Make sure you're using a polyfill."; if (n) for (a = 0; a < n.length; a++) { var u = i.matchMedia(n[a][0]); u.addListener(h), l.push(u) } return h(), { removeMediaQueryListeners: function () { l.forEach((function (e) { e.removeListener(h) })) }, getCurrentOptions: function () { return t.extend({}, r) } } }, t.splitIntoSegments = function (e, i, n) { n = t.extend({}, { increasingX: !1, fillHoles: !1 }, n); for (var s = [], r = !0, a = 0; a < e.length; a += 2)void 0 === t.getMultiValue(i[a / 2].value) ? n.fillHoles || (r = !0) : (n.increasingX && a >= 2 && e[a] <= e[a - 2] && (r = !0), r && (s.push({ pathCoordinates: [], valueData: [] }), r = !1), s[s.length - 1].pathCoordinates.push(e[a], e[a + 1]), s[s.length - 1].valueData.push(i[a / 2])); return s } }(this || global, e), function (e, t) { "use strict"; t.Interpolation = {}, t.Interpolation.none = function (e) { return e = t.extend({}, { fillHoles: !1 }, e), function (i, n) { for (var s = new t.Svg.Path, r = !0, a = 0; a < i.length; a += 2) { var o = i[a], l = i[a + 1], h = n[a / 2]; void 0 !== t.getMultiValue(h.value) ? (r ? s.move(o, l, !1, h) : s.line(o, l, !1, h), r = !1) : e.fillHoles || (r = !0) } return s } }, t.Interpolation.simple = function (e) { e = t.extend({}, { divisor: 2, fillHoles: !1 }, e); var i = 1 / Math.max(1, e.divisor); return function (n, s) { for (var r, a, o, l = new t.Svg.Path, h = 0; h < n.length; h += 2) { var u = n[h], c = n[h + 1], d = (u - r) * i, p = s[h / 2]; void 0 !== p.value ? (void 0 === o ? l.move(u, c, !1, p) : l.curve(r + d, a, u - d, c, u, c, !1, p), r = u, a = c, o = p) : e.fillHoles || (r = u = o = void 0) } return l } }, t.Interpolation.cardinal = function (e) { e = t.extend({}, { tension: 1, fillHoles: !1 }, e); var i = Math.min(1, Math.max(0, e.tension)), n = 1 - i; return function s(r, a) { var o = t.splitIntoSegments(r, a, { fillHoles: e.fillHoles }); if (o.length) { if (o.length > 1) { var l = []; return o.forEach((function (e) { l.push(s(e.pathCoordinates, e.valueData)) })), t.Svg.Path.join(l) } if (r = o[0].pathCoordinates, a = o[0].valueData, r.length <= 4) return t.Interpolation.none()(r, a); for (var h = (new t.Svg.Path).move(r[0], r[1], !1, a[0]), u = 0, c = r.length; c - 2 > u; u += 2) { var d = [{ x: +r[u - 2], y: +r[u - 1] }, { x: +r[u], y: +r[u + 1] }, { x: +r[u + 2], y: +r[u + 3] }, { x: +r[u + 4], y: +r[u + 5] }]; c - 4 === u ? d[3] = d[2] : u || (d[0] = { x: +r[u], y: +r[u + 1] }), h.curve(i * (-d[0].x + 6 * d[1].x + d[2].x) / 6 + n * d[2].x, i * (-d[0].y + 6 * d[1].y + d[2].y) / 6 + n * d[2].y, i * (d[1].x + 6 * d[2].x - d[3].x) / 6 + n * d[2].x, i * (d[1].y + 6 * d[2].y - d[3].y) / 6 + n * d[2].y, d[2].x, d[2].y, !1, a[(u + 2) / 2]) } return h } return t.Interpolation.none()([]) } }, t.Interpolation.monotoneCubic = function (e) { return e = t.extend({}, { fillHoles: !1 }, e), function i(n, s) { var r = t.splitIntoSegments(n, s, { fillHoles: e.fillHoles, increasingX: !0 }); if (r.length) { if (r.length > 1) { var a = []; return r.forEach((function (e) { a.push(i(e.pathCoordinates, e.valueData)) })), t.Svg.Path.join(a) } if (n = r[0].pathCoordinates, s = r[0].valueData, n.length <= 4) return t.Interpolation.none()(n, s); var o, l, h = [], u = [], c = n.length / 2, d = [], p = [], f = [], m = []; for (o = 0; o < c; o++)h[o] = n[2 * o], u[o] = n[2 * o + 1]; for (o = 0; o < c - 1; o++)f[o] = u[o + 1] - u[o], m[o] = h[o + 1] - h[o], p[o] = f[o] / m[o]; for (d[0] = p[0], d[c - 1] = p[c - 2], o = 1; o < c - 1; o++)0 === p[o] || 0 === p[o - 1] || p[o - 1] > 0 != p[o] > 0 ? d[o] = 0 : (d[o] = 3 * (m[o - 1] + m[o]) / ((2 * m[o] + m[o - 1]) / p[o - 1] + (m[o] + 2 * m[o - 1]) / p[o]), isFinite(d[o]) || (d[o] = 0)); for (l = (new t.Svg.Path).move(h[0], u[0], !1, s[0]), o = 0; o < c - 1; o++)l.curve(h[o] + m[o] / 3, u[o] + d[o] * m[o] / 3, h[o + 1] - m[o] / 3, u[o + 1] - d[o + 1] * m[o] / 3, h[o + 1], u[o + 1], !1, s[o + 1]); return l } return t.Interpolation.none()([]) } }, t.Interpolation.step = function (e) { return e = t.extend({}, { postpone: !0, fillHoles: !1 }, e), function (i, n) { for (var s, r, a, o = new t.Svg.Path, l = 0; l < i.length; l += 2) { var h = i[l], u = i[l + 1], c = n[l / 2]; void 0 !== c.value ? (void 0 === a ? o.move(h, u, !1, c) : (e.postpone ? o.line(h, r, !1, a) : o.line(s, u, !1, c), o.line(h, u, !1, c)), s = h, r = u, a = c) : e.fillHoles || (s = r = a = void 0) } return o } } }(this || global, e), function (e, t) { "use strict"; t.EventEmitter = function () { var e = []; return { addEventHandler: function (t, i) { e[t] = e[t] || [], e[t].push(i) }, removeEventHandler: function (t, i) { e[t] && (i ? (e[t].splice(e[t].indexOf(i), 1), 0 === e[t].length && delete e[t]) : delete e[t]) }, emit: function (t, i) { e[t] && e[t].forEach((function (e) { e(i) })), e["*"] && e["*"].forEach((function (e) { e(t, i) })) } } } }(this || global, e), function (e, t) { "use strict"; t.Class = { extend: function (e, i) { var n = i || this.prototype || t.Class, s = Object.create(n); t.Class.cloneDefinitions(s, e); var r = function () { var e, i = s.constructor || function () { }; return e = this === t ? Object.create(s) : this, i.apply(e, Array.prototype.slice.call(arguments, 0)), e }; return r.prototype = s, r.super = n, r.extend = this.extend, r }, cloneDefinitions: function () { var e = function (e) { var t = []; if (e.length) for (var i = 0; i < e.length; i++)t.push(e[i]); return t }(arguments), t = e[0]; return e.splice(1, e.length - 1).forEach((function (e) { Object.getOwnPropertyNames(e).forEach((function (i) { delete t[i], Object.defineProperty(t, i, Object.getOwnPropertyDescriptor(e, i)) })) })), t } } }(this || global, e), function (e, t) { "use strict"; var i = e.window; function n() { i.addEventListener("resize", this.resizeListener), this.optionsProvider = t.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter), this.eventEmitter.addEventHandler("optionsChanged", function () { this.update() }.bind(this)), this.options.plugins && this.options.plugins.forEach(function (e) { e instanceof Array ? e[0](this, e[1]) : e(this) }.bind(this)), this.eventEmitter.emit("data", { type: "initial", data: this.data }), this.createChart(this.optionsProvider.getCurrentOptions()), this.initializeTimeoutId = void 0 } t.Base = t.Class.extend({ constructor: function (e, i, s, r, a) { this.container = t.querySelector(e), this.data = i || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.defaultOptions = s, this.options = r, this.responsiveOptions = a, this.eventEmitter = t.EventEmitter(), this.supportsForeignObject = t.Svg.isSupported("Extensibility"), this.supportsAnimations = t.Svg.isSupported("AnimationEventsAttribute"), this.resizeListener = function () { this.update() }.bind(this), this.container && (this.container.__chartist__ && this.container.__chartist__.detach(), this.container.__chartist__ = this), this.initializeTimeoutId = setTimeout(n.bind(this), 0) }, optionsProvider: void 0, container: void 0, svg: void 0, eventEmitter: void 0, createChart: function () { throw new Error("Base chart type can't be instantiated!") }, update: function (e, i, n) { return e && (this.data = e || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.eventEmitter.emit("data", { type: "update", data: this.data })), i && (this.options = t.extend({}, n ? this.options : this.defaultOptions, i), this.initializeTimeoutId || (this.optionsProvider.removeMediaQueryListeners(), this.optionsProvider = t.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter))), this.initializeTimeoutId || this.createChart(this.optionsProvider.getCurrentOptions()), this }, detach: function () { return this.initializeTimeoutId ? i.clearTimeout(this.initializeTimeoutId) : (i.removeEventListener("resize", this.resizeListener), this.optionsProvider.removeMediaQueryListeners()), this }, on: function (e, t) { return this.eventEmitter.addEventHandler(e, t), this }, off: function (e, t) { return this.eventEmitter.removeEventHandler(e, t), this }, version: t.version, supportsForeignObject: !1 }) }(this || global, e), function (e, t) { "use strict"; var i = e.document; t.Svg = t.Class.extend({ constructor: function (e, n, s, r, a) { e instanceof Element ? this._node = e : (this._node = i.createElementNS(t.namespaces.svg, e), "svg" === e && this.attr({ "xmlns:ct": t.namespaces.ct })), n && this.attr(n), s && this.addClass(s), r && (a && r._node.firstChild ? r._node.insertBefore(this._node, r._node.firstChild) : r._node.appendChild(this._node)) }, attr: function (e, i) { return "string" == typeof e ? i ? this._node.getAttributeNS(i, e) : this._node.getAttribute(e) : (Object.keys(e).forEach(function (i) { if (void 0 !== e[i]) if (-1 !== i.indexOf(":")) { var n = i.split(":"); this._node.setAttributeNS(t.namespaces[n[0]], i, e[i]) } else this._node.setAttribute(i, e[i]) }.bind(this)), this) }, elem: function (e, i, n, s) { return new t.Svg(e, i, n, this, s) }, parent: function () { return this._node.parentNode instanceof SVGElement ? new t.Svg(this._node.parentNode) : null }, root: function () { for (var e = this._node; "svg" !== e.nodeName;)e = e.parentNode; return new t.Svg(e) }, querySelector: function (e) { var i = this._node.querySelector(e); return i ? new t.Svg(i) : null }, querySelectorAll: function (e) { var i = this._node.querySelectorAll(e); return i.length ? new t.Svg.List(i) : null }, getNode: function () { return this._node }, foreignObject: function (e, n, s, r) { if ("string" == typeof e) { var a = i.createElement("div"); a.innerHTML = e, e = a.firstChild } e.setAttribute("xmlns", t.namespaces.xmlns); var o = this.elem("foreignObject", n, s, r); return o._node.appendChild(e), o }, text: function (e) { return this._node.appendChild(i.createTextNode(e)), this }, empty: function () { for (; this._node.firstChild;)this._node.removeChild(this._node.firstChild); return this }, remove: function () { return this._node.parentNode.removeChild(this._node), this.parent() }, replace: function (e) { return this._node.parentNode.replaceChild(e._node, this._node), e }, append: function (e, t) { return t && this._node.firstChild ? this._node.insertBefore(e._node, this._node.firstChild) : this._node.appendChild(e._node), this }, classes: function () { return this._node.getAttribute("class") ? this._node.getAttribute("class").trim().split(/\s+/) : [] }, addClass: function (e) { return this._node.setAttribute("class", this.classes(this._node).concat(e.trim().split(/\s+/)).filter((function (e, t, i) { return i.indexOf(e) === t })).join(" ")), this }, removeClass: function (e) { var t = e.trim().split(/\s+/); return this._node.setAttribute("class", this.classes(this._node).filter((function (e) { return -1 === t.indexOf(e) })).join(" ")), this }, removeAllClasses: function () { return this._node.setAttribute("class", ""), this }, height: function () { return this._node.getBoundingClientRect().height }, width: function () { return this._node.getBoundingClientRect().width }, animate: function (e, i, n) { return void 0 === i && (i = !0), Object.keys(e).forEach(function (s) { function r(e, i) { var r, a, o, l = {}; e.easing && (o = e.easing instanceof Array ? e.easing : t.Svg.Easing[e.easing], delete e.easing), e.begin = t.ensureUnit(e.begin, "ms"), e.dur = t.ensureUnit(e.dur, "ms"), o && (e.calcMode = "spline", e.keySplines = o.join(" "), e.keyTimes = "0;1"), i && (e.fill = "freeze", l[s] = e.from, this.attr(l), a = t.quantity(e.begin || 0).value, e.begin = "indefinite"), r = this.elem("animate", t.extend({ attributeName: s }, e)), i && setTimeout(function () { try { r._node.beginElement() } catch (t) { l[s] = e.to, this.attr(l), r.remove() } }.bind(this), a), n && r._node.addEventListener("beginEvent", function () { n.emit("animationBegin", { element: this, animate: r._node, params: e }) }.bind(this)), r._node.addEventListener("endEvent", function () { n && n.emit("animationEnd", { element: this, animate: r._node, params: e }), i && (l[s] = e.to, this.attr(l), r.remove()) }.bind(this)) } e[s] instanceof Array ? e[s].forEach(function (e) { r.bind(this)(e, !1) }.bind(this)) : r.bind(this)(e[s], i) }.bind(this)), this } }), t.Svg.isSupported = function (e) { return i.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#" + e, "1.1") }; t.Svg.Easing = { easeInSine: [.47, 0, .745, .715], easeOutSine: [.39, .575, .565, 1], easeInOutSine: [.445, .05, .55, .95], easeInQuad: [.55, .085, .68, .53], easeOutQuad: [.25, .46, .45, .94], easeInOutQuad: [.455, .03, .515, .955], easeInCubic: [.55, .055, .675, .19], easeOutCubic: [.215, .61, .355, 1], easeInOutCubic: [.645, .045, .355, 1], easeInQuart: [.895, .03, .685, .22], easeOutQuart: [.165, .84, .44, 1], easeInOutQuart: [.77, 0, .175, 1], easeInQuint: [.755, .05, .855, .06], easeOutQuint: [.23, 1, .32, 1], easeInOutQuint: [.86, 0, .07, 1], easeInExpo: [.95, .05, .795, .035], easeOutExpo: [.19, 1, .22, 1], easeInOutExpo: [1, 0, 0, 1], easeInCirc: [.6, .04, .98, .335], easeOutCirc: [.075, .82, .165, 1], easeInOutCirc: [.785, .135, .15, .86], easeInBack: [.6, -.28, .735, .045], easeOutBack: [.175, .885, .32, 1.275], easeInOutBack: [.68, -.55, .265, 1.55] }, t.Svg.List = t.Class.extend({ constructor: function (e) { var i = this; this.svgElements = []; for (var n = 0; n < e.length; n++)this.svgElements.push(new t.Svg(e[n])); Object.keys(t.Svg.prototype).filter((function (e) { return -1 === ["constructor", "parent", "querySelector", "querySelectorAll", "replace", "append", "classes", "height", "width"].indexOf(e) })).forEach((function (e) { i[e] = function () { var n = Array.prototype.slice.call(arguments, 0); return i.svgElements.forEach((function (i) { t.Svg.prototype[e].apply(i, n) })), i } })) } }) }(this || global, e), function (e, t) { "use strict"; var i = { m: ["x", "y"], l: ["x", "y"], c: ["x1", "y1", "x2", "y2", "x", "y"], a: ["rx", "ry", "xAr", "lAf", "sf", "x", "y"] }, n = { accuracy: 3 }; function s(e, i, n, s, r, a) { var o = t.extend({ command: r ? e.toLowerCase() : e.toUpperCase() }, i, a ? { data: a } : {}); n.splice(s, 0, o) } function r(e, t) { e.forEach((function (n, s) { i[n.command.toLowerCase()].forEach((function (i, r) { t(n, i, s, r, e) })) })) } t.Svg.Path = t.Class.extend({ constructor: function (e, i) { this.pathElements = [], this.pos = 0, this.close = e, this.options = t.extend({}, n, i) }, position: function (e) { return void 0 !== e ? (this.pos = Math.max(0, Math.min(this.pathElements.length, e)), this) : this.pos }, remove: function (e) { return this.pathElements.splice(this.pos, e), this }, move: function (e, t, i, n) { return s("M", { x: +e, y: +t }, this.pathElements, this.pos++, i, n), this }, line: function (e, t, i, n) { return s("L", { x: +e, y: +t }, this.pathElements, this.pos++, i, n), this }, curve: function (e, t, i, n, r, a, o, l) { return s("C", { x1: +e, y1: +t, x2: +i, y2: +n, x: +r, y: +a }, this.pathElements, this.pos++, o, l), this }, arc: function (e, t, i, n, r, a, o, l, h) { return s("A", { rx: +e, ry: +t, xAr: +i, lAf: +n, sf: +r, x: +a, y: +o }, this.pathElements, this.pos++, l, h), this }, scale: function (e, t) { return r(this.pathElements, (function (i, n) { i[n] *= "x" === n[0] ? e : t })), this }, translate: function (e, t) { return r(this.pathElements, (function (i, n) { i[n] += "x" === n[0] ? e : t })), this }, transform: function (e) { return r(this.pathElements, (function (t, i, n, s, r) { var a = e(t, i, n, s, r); (a || 0 === a) && (t[i] = a) })), this }, parse: function (e) { var n = e.replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/([0-9])([A-Za-z])/g, "$1 $2").split(/[\s,]+/).reduce((function (e, t) { return t.match(/[A-Za-z]/) && e.push([]), e[e.length - 1].push(t), e }), []); "Z" === n[n.length - 1][0].toUpperCase() && n.pop(); var s = n.map((function (e) { var n = e.shift(), s = i[n.toLowerCase()]; return t.extend({ command: n }, s.reduce((function (t, i, n) { return t[i] = +e[n], t }), {})) })), r = [this.pos, 0]; return Array.prototype.push.apply(r, s), Array.prototype.splice.apply(this.pathElements, r), this.pos += s.length, this }, stringify: function () { var e = Math.pow(10, this.options.accuracy); return this.pathElements.reduce(function (t, n) { var s = i[n.command.toLowerCase()].map(function (t) { return this.options.accuracy ? Math.round(n[t] * e) / e : n[t] }.bind(this)); return t + n.command + s.join(",") }.bind(this), "") + (this.close ? "Z" : "") }, clone: function (e) { var i = new t.Svg.Path(e || this.close); return i.pos = this.pos, i.pathElements = this.pathElements.slice().map((function (e) { return t.extend({}, e) })), i.options = t.extend({}, this.options), i }, splitByCommand: function (e) { var i = [new t.Svg.Path]; return this.pathElements.forEach((function (n) { n.command === e.toUpperCase() && 0 !== i[i.length - 1].pathElements.length && i.push(new t.Svg.Path), i[i.length - 1].pathElements.push(n) })), i } }), t.Svg.Path.elementDescriptions = i, t.Svg.Path.join = function (e, i, n) { for (var s = new t.Svg.Path(i, n), r = 0; r < e.length; r++)for (var a = e[r], o = 0; o < a.pathElements.length; o++)s.pathElements.push(a.pathElements[o]); return s } }(this || global, e), function (e, t) { "use strict"; e.window, e.document; var i = { x: { pos: "x", len: "width", dir: "horizontal", rectStart: "x1", rectEnd: "x2", rectOffset: "y2" }, y: { pos: "y", len: "height", dir: "vertical", rectStart: "y2", rectEnd: "y1", rectOffset: "x1" } }; t.Axis = t.Class.extend({ constructor: function (e, t, n, s) { this.units = e, this.counterUnits = e === i.x ? i.y : i.x, this.chartRect = t, this.axisLength = t[e.rectEnd] - t[e.rectStart], this.gridOffset = t[e.rectOffset], this.ticks = n, this.options = s }, createGridAndLabels: function (e, i, n, s, r) { var a = s["axis" + this.units.pos.toUpperCase()], o = this.ticks.map(this.projectValue.bind(this)), l = this.ticks.map(a.labelInterpolationFnc); o.forEach(function (h, u) { var c, d = { x: 0, y: 0 }; c = o[u + 1] ? o[u + 1] - h : Math.max(this.axisLength - h, 30), t.isFalseyButZero(l[u]) && "" !== l[u] || ("x" === this.units.pos ? (h = this.chartRect.x1 + h, d.x = s.axisX.labelOffset.x, "start" === s.axisX.position ? d.y = this.chartRect.padding.top + s.axisX.labelOffset.y + (n ? 5 : 20) : d.y = this.chartRect.y1 + s.axisX.labelOffset.y + (n ? 5 : 20)) : (h = this.chartRect.y1 - h, d.y = s.axisY.labelOffset.y - (n ? c : 0), "start" === s.axisY.position ? d.x = n ? this.chartRect.padding.left + s.axisY.labelOffset.x : this.chartRect.x1 - 10 : d.x = this.chartRect.x2 + s.axisY.labelOffset.x + 10), a.showGrid && t.createGrid(h, u, this, this.gridOffset, this.chartRect[this.counterUnits.len](), e, [s.classNames.grid, s.classNames[this.units.dir]], r), a.showLabel && t.createLabel(h, c, u, l, this, a.offset, d, i, [s.classNames.label, s.classNames[this.units.dir], "start" === a.position ? s.classNames[a.position] : s.classNames.end], n, r)) }.bind(this)) }, projectValue: function (e, t, i) { throw new Error("Base axis can't be instantiated!") } }), t.Axis.units = i }(this || global, e), function (e, t) { "use strict"; e.window, e.document; t.AutoScaleAxis = t.Axis.extend({ constructor: function (e, i, n, s) { var r = s.highLow || t.getHighLow(i, s, e.pos); this.bounds = t.getBounds(n[e.rectEnd] - n[e.rectStart], r, s.scaleMinSpace || 20, s.onlyInteger), this.range = { min: this.bounds.min, max: this.bounds.max }, t.AutoScaleAxis.super.constructor.call(this, e, n, this.bounds.values, s) }, projectValue: function (e) { return this.axisLength * (+t.getMultiValue(e, this.units.pos) - this.bounds.min) / this.bounds.range } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; t.FixedScaleAxis = t.Axis.extend({ constructor: function (e, i, n, s) { var r = s.highLow || t.getHighLow(i, s, e.pos); this.divisor = s.divisor || 1, this.ticks = s.ticks || t.times(this.divisor).map(function (e, t) { return r.low + (r.high - r.low) / this.divisor * t }.bind(this)), this.ticks.sort((function (e, t) { return e - t })), this.range = { min: r.low, max: r.high }, t.FixedScaleAxis.super.constructor.call(this, e, n, this.ticks, s), this.stepLength = this.axisLength / this.divisor }, projectValue: function (e) { return this.axisLength * (+t.getMultiValue(e, this.units.pos) - this.range.min) / (this.range.max - this.range.min) } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; t.StepAxis = t.Axis.extend({ constructor: function (e, i, n, s) { t.StepAxis.super.constructor.call(this, e, n, s.ticks, s); var r = Math.max(1, s.ticks.length - (s.stretch ? 1 : 0)); this.stepLength = this.axisLength / r }, projectValue: function (e, t) { return this.stepLength * t } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; var i = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: t.noop, type: void 0 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: t.noop, type: void 0, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, showLine: !0, showPoint: !0, showArea: !1, areaBase: 0, lineSmooth: !0, showGridBackground: !1, low: void 0, high: void 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, fullWidth: !1, reverseData: !1, classNames: { chart: "ct-chart-line", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", line: "ct-line", point: "ct-point", area: "ct-area", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; t.Line = t.Base.extend({ constructor: function (e, n, s, r) { t.Line.super.constructor.call(this, e, n, i, t.extend({}, i, s), r) }, createChart: function (e) { var n = t.normalizeData(this.data, e.reverseData, !0); this.svg = t.createSvg(this.container, e.width, e.height, e.classNames.chart); var s, r, a = this.svg.elem("g").addClass(e.classNames.gridGroup), o = this.svg.elem("g"), l = this.svg.elem("g").addClass(e.classNames.labelGroup), h = t.createChartRect(this.svg, e, i.padding); s = void 0 === e.axisX.type ? new t.StepAxis(t.Axis.units.x, n.normalized.series, h, t.extend({}, e.axisX, { ticks: n.normalized.labels, stretch: e.fullWidth })) : e.axisX.type.call(t, t.Axis.units.x, n.normalized.series, h, e.axisX), r = void 0 === e.axisY.type ? new t.AutoScaleAxis(t.Axis.units.y, n.normalized.series, h, t.extend({}, e.axisY, { high: t.isNumeric(e.high) ? e.high : e.axisY.high, low: t.isNumeric(e.low) ? e.low : e.axisY.low })) : e.axisY.type.call(t, t.Axis.units.y, n.normalized.series, h, e.axisY), s.createGridAndLabels(a, l, this.supportsForeignObject, e, this.eventEmitter), r.createGridAndLabels(a, l, this.supportsForeignObject, e, this.eventEmitter), e.showGridBackground && t.createGridBackground(a, h, e.classNames.gridBackground, this.eventEmitter), n.raw.series.forEach(function (i, a) { var l = o.elem("g"); l.attr({ "ct:series-name": i.name, "ct:meta": t.serialize(i.meta) }), l.addClass([e.classNames.series, i.className || e.classNames.series + "-" + t.alphaNumerate(a)].join(" ")); var u = [], c = []; n.normalized.series[a].forEach(function (e, o) { var l = { x: h.x1 + s.projectValue(e, o, n.normalized.series[a]), y: h.y1 - r.projectValue(e, o, n.normalized.series[a]) }; u.push(l.x, l.y), c.push({ value: e, valueIndex: o, meta: t.getMetaData(i, o) }) }.bind(this)); var d = { lineSmooth: t.getSeriesOption(i, e, "lineSmooth"), showPoint: t.getSeriesOption(i, e, "showPoint"), showLine: t.getSeriesOption(i, e, "showLine"), showArea: t.getSeriesOption(i, e, "showArea"), areaBase: t.getSeriesOption(i, e, "areaBase") }, p = ("function" == typeof d.lineSmooth ? d.lineSmooth : d.lineSmooth ? t.Interpolation.monotoneCubic() : t.Interpolation.none())(u, c); if (d.showPoint && p.pathElements.forEach(function (n) { var o = l.elem("line", { x1: n.x, y1: n.y, x2: n.x + .01, y2: n.y }, e.classNames.point).attr({ "ct:value": [n.data.value.x, n.data.value.y].filter(t.isNumeric).join(","), "ct:meta": t.serialize(n.data.meta) }); this.eventEmitter.emit("draw", { type: "point", value: n.data.value, index: n.data.valueIndex, meta: n.data.meta, series: i, seriesIndex: a, axisX: s, axisY: r, group: l, element: o, x: n.x, y: n.y }) }.bind(this)), d.showLine) { var f = l.elem("path", { d: p.stringify() }, e.classNames.line, !0); this.eventEmitter.emit("draw", { type: "line", values: n.normalized.series[a], path: p.clone(), chartRect: h, index: a, series: i, seriesIndex: a, seriesMeta: i.meta, axisX: s, axisY: r, group: l, element: f }) } if (d.showArea && r.range) { var m = Math.max(Math.min(d.areaBase, r.range.max), r.range.min), g = h.y1 - r.projectValue(m); p.splitByCommand("M").filter((function (e) { return e.pathElements.length > 1 })).map((function (e) { var t = e.pathElements[0], i = e.pathElements[e.pathElements.length - 1]; return e.clone(!0).position(0).remove(1).move(t.x, g).line(t.x, t.y).position(e.pathElements.length + 1).line(i.x, g) })).forEach(function (t) { var o = l.elem("path", { d: t.stringify() }, e.classNames.area, !0); this.eventEmitter.emit("draw", { type: "area", values: n.normalized.series[a], path: t.clone(), series: i, seriesIndex: a, axisX: s, axisY: r, chartRect: h, index: a, group: l, element: o }) }.bind(this)) } }.bind(this)), this.eventEmitter.emit("created", { bounds: r.bounds, chartRect: h, axisX: s, axisY: r, svg: this.svg, options: e }) } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; var i = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: t.noop, scaleMinSpace: 30, onlyInteger: !1 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: t.noop, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, high: void 0, low: void 0, referenceValue: 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, seriesBarDistance: 15, stackBars: !1, stackMode: "accumulate", horizontalBars: !1, distributeSeries: !1, reverseData: !1, showGridBackground: !1, classNames: { chart: "ct-chart-bar", horizontalBars: "ct-horizontal-bars", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", bar: "ct-bar", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; t.Bar = t.Base.extend({ constructor: function (e, n, s, r) { t.Bar.super.constructor.call(this, e, n, i, t.extend({}, i, s), r) }, createChart: function (e) { var n, s; e.distributeSeries ? (n = t.normalizeData(this.data, e.reverseData, e.horizontalBars ? "x" : "y")).normalized.series = n.normalized.series.map((function (e) { return [e] })) : n = t.normalizeData(this.data, e.reverseData, e.horizontalBars ? "x" : "y"), this.svg = t.createSvg(this.container, e.width, e.height, e.classNames.chart + (e.horizontalBars ? " " + e.classNames.horizontalBars : "")); var r = this.svg.elem("g").addClass(e.classNames.gridGroup), a = this.svg.elem("g"), o = this.svg.elem("g").addClass(e.classNames.labelGroup); if (e.stackBars && 0 !== n.normalized.series.length) { var l = t.serialMap(n.normalized.series, (function () { return Array.prototype.slice.call(arguments).map((function (e) { return e })).reduce((function (e, t) { return { x: e.x + (t && t.x) || 0, y: e.y + (t && t.y) || 0 } }), { x: 0, y: 0 }) })); s = t.getHighLow([l], e, e.horizontalBars ? "x" : "y") } else s = t.getHighLow(n.normalized.series, e, e.horizontalBars ? "x" : "y"); s.high = +e.high || (0 === e.high ? 0 : s.high), s.low = +e.low || (0 === e.low ? 0 : s.low); var h, u, c, d, p, f = t.createChartRect(this.svg, e, i.padding); u = e.distributeSeries && e.stackBars ? n.normalized.labels.slice(0, 1) : n.normalized.labels, e.horizontalBars ? (h = d = void 0 === e.axisX.type ? new t.AutoScaleAxis(t.Axis.units.x, n.normalized.series, f, t.extend({}, e.axisX, { highLow: s, referenceValue: 0 })) : e.axisX.type.call(t, t.Axis.units.x, n.normalized.series, f, t.extend({}, e.axisX, { highLow: s, referenceValue: 0 })), c = p = void 0 === e.axisY.type ? new t.StepAxis(t.Axis.units.y, n.normalized.series, f, { ticks: u }) : e.axisY.type.call(t, t.Axis.units.y, n.normalized.series, f, e.axisY)) : (c = d = void 0 === e.axisX.type ? new t.StepAxis(t.Axis.units.x, n.normalized.series, f, { ticks: u }) : e.axisX.type.call(t, t.Axis.units.x, n.normalized.series, f, e.axisX), h = p = void 0 === e.axisY.type ? new t.AutoScaleAxis(t.Axis.units.y, n.normalized.series, f, t.extend({}, e.axisY, { highLow: s, referenceValue: 0 })) : e.axisY.type.call(t, t.Axis.units.y, n.normalized.series, f, t.extend({}, e.axisY, { highLow: s, referenceValue: 0 }))); var m = e.horizontalBars ? f.x1 + h.projectValue(0) : f.y1 - h.projectValue(0), g = []; c.createGridAndLabels(r, o, this.supportsForeignObject, e, this.eventEmitter), h.createGridAndLabels(r, o, this.supportsForeignObject, e, this.eventEmitter), e.showGridBackground && t.createGridBackground(r, f, e.classNames.gridBackground, this.eventEmitter), n.raw.series.forEach(function (i, s) { var r, o, l = s - (n.raw.series.length - 1) / 2; r = e.distributeSeries && !e.stackBars ? c.axisLength / n.normalized.series.length / 2 : e.distributeSeries && e.stackBars ? c.axisLength / 2 : c.axisLength / n.normalized.series[s].length / 2, (o = a.elem("g")).attr({ "ct:series-name": i.name, "ct:meta": t.serialize(i.meta) }), o.addClass([e.classNames.series, i.className || e.classNames.series + "-" + t.alphaNumerate(s)].join(" ")), n.normalized.series[s].forEach(function (a, u) { var v, x, y, b; if (b = e.distributeSeries && !e.stackBars ? s : e.distributeSeries && e.stackBars ? 0 : u, v = e.horizontalBars ? { x: f.x1 + h.projectValue(a && a.x ? a.x : 0, u, n.normalized.series[s]), y: f.y1 - c.projectValue(a && a.y ? a.y : 0, b, n.normalized.series[s]) } : { x: f.x1 + c.projectValue(a && a.x ? a.x : 0, b, n.normalized.series[s]), y: f.y1 - h.projectValue(a && a.y ? a.y : 0, u, n.normalized.series[s]) }, c instanceof t.StepAxis && (c.options.stretch || (v[c.units.pos] += r * (e.horizontalBars ? -1 : 1)), v[c.units.pos] += e.stackBars || e.distributeSeries ? 0 : l * e.seriesBarDistance * (e.horizontalBars ? -1 : 1)), y = g[u] || m, g[u] = y - (m - v[c.counterUnits.pos]), void 0 !== a) { var w = {}; w[c.units.pos + "1"] = v[c.units.pos], w[c.units.pos + "2"] = v[c.units.pos], !e.stackBars || "accumulate" !== e.stackMode && e.stackMode ? (w[c.counterUnits.pos + "1"] = m, w[c.counterUnits.pos + "2"] = v[c.counterUnits.pos]) : (w[c.counterUnits.pos + "1"] = y, w[c.counterUnits.pos + "2"] = g[u]), w.x1 = Math.min(Math.max(w.x1, f.x1), f.x2), w.x2 = Math.min(Math.max(w.x2, f.x1), f.x2), w.y1 = Math.min(Math.max(w.y1, f.y2), f.y1), w.y2 = Math.min(Math.max(w.y2, f.y2), f.y1); var E = t.getMetaData(i, u); x = o.elem("line", w, e.classNames.bar).attr({ "ct:value": [a.x, a.y].filter(t.isNumeric).join(","), "ct:meta": t.serialize(E) }), this.eventEmitter.emit("draw", t.extend({ type: "bar", value: a, index: u, meta: E, series: i, seriesIndex: s, axisX: d, axisY: p, chartRect: f, group: o, element: x }, w)) } }.bind(this)) }.bind(this)), this.eventEmitter.emit("created", { bounds: h.bounds, chartRect: f, axisX: d, axisY: p, svg: this.svg, options: e }) } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; var i = { width: void 0, height: void 0, chartPadding: 5, classNames: { chartPie: "ct-chart-pie", chartDonut: "ct-chart-donut", series: "ct-series", slicePie: "ct-slice-pie", sliceDonut: "ct-slice-donut", sliceDonutSolid: "ct-slice-donut-solid", label: "ct-label" }, startAngle: 0, total: void 0, donut: !1, donutSolid: !1, donutWidth: 60, showLabel: !0, labelOffset: 0, labelPosition: "inside", labelInterpolationFnc: t.noop, labelDirection: "neutral", reverseData: !1, ignoreEmptyValues: !1 }; function n(e, t, i) { var n = t.x > e.x; return n && "explode" === i || !n && "implode" === i ? "start" : n && "implode" === i || !n && "explode" === i ? "end" : "middle" } t.Pie = t.Base.extend({ constructor: function (e, n, s, r) { t.Pie.super.constructor.call(this, e, n, i, t.extend({}, i, s), r) }, createChart: function (e) { var s, r, a, o, l, h = t.normalizeData(this.data), u = [], c = e.startAngle; this.svg = t.createSvg(this.container, e.width, e.height, e.donut ? e.classNames.chartDonut : e.classNames.chartPie), r = t.createChartRect(this.svg, e, i.padding), a = Math.min(r.width() / 2, r.height() / 2), l = e.total || h.normalized.series.reduce((function (e, t) { return e + t }), 0); var d = t.quantity(e.donutWidth); "%" === d.unit && (d.value *= a / 100), a -= e.donut && !e.donutSolid ? d.value / 2 : 0, o = "outside" === e.labelPosition || e.donut && !e.donutSolid ? a : "center" === e.labelPosition ? 0 : e.donutSolid ? a - d.value / 2 : a / 2, o += e.labelOffset; var p = { x: r.x1 + r.width() / 2, y: r.y2 + r.height() / 2 }, f = 1 === h.raw.series.filter((function (e) { return e.hasOwnProperty("value") ? 0 !== e.value : 0 !== e })).length; h.raw.series.forEach(function (e, t) { u[t] = this.svg.elem("g", null, null) }.bind(this)), e.showLabel && (s = this.svg.elem("g", null, null)), h.raw.series.forEach(function (i, r) { if (0 !== h.normalized.series[r] || !e.ignoreEmptyValues) { u[r].attr({ "ct:series-name": i.name }), u[r].addClass([e.classNames.series, i.className || e.classNames.series + "-" + t.alphaNumerate(r)].join(" ")); var m = l > 0 ? c + h.normalized.series[r] / l * 360 : 0, g = Math.max(0, c - (0 === r || f ? 0 : .2)); m - g >= 359.99 && (m = g + 359.99); var v, x, y, b = t.polarToCartesian(p.x, p.y, a, g), w = t.polarToCartesian(p.x, p.y, a, m), E = new t.Svg.Path(!e.donut || e.donutSolid).move(w.x, w.y).arc(a, a, 0, m - c > 180, 0, b.x, b.y); e.donut ? e.donutSolid && (y = a - d.value, v = t.polarToCartesian(p.x, p.y, y, c - (0 === r || f ? 0 : .2)), x = t.polarToCartesian(p.x, p.y, y, m), E.line(v.x, v.y), E.arc(y, y, 0, m - c > 180, 1, x.x, x.y)) : E.line(p.x, p.y); var S = e.classNames.slicePie; e.donut && (S = e.classNames.sliceDonut, e.donutSolid && (S = e.classNames.sliceDonutSolid)); var A = u[r].elem("path", { d: E.stringify() }, S); if (A.attr({ "ct:value": h.normalized.series[r], "ct:meta": t.serialize(i.meta) }), e.donut && !e.donutSolid && (A._node.style.strokeWidth = d.value + "px"), this.eventEmitter.emit("draw", { type: "slice", value: h.normalized.series[r], totalDataSum: l, index: r, meta: i.meta, series: i, group: u[r], element: A, path: E.clone(), center: p, radius: a, startAngle: c, endAngle: m }), e.showLabel) { var z, M; z = 1 === h.raw.series.length ? { x: p.x, y: p.y } : t.polarToCartesian(p.x, p.y, o, c + (m - c) / 2), M = h.normalized.labels && !t.isFalseyButZero(h.normalized.labels[r]) ? h.normalized.labels[r] : h.normalized.series[r]; var O = e.labelInterpolationFnc(M, r); if (O || 0 === O) { var C = s.elem("text", { dx: z.x, dy: z.y, "text-anchor": n(p, z, e.labelDirection) }, e.classNames.label).text("" + O); this.eventEmitter.emit("draw", { type: "label", index: r, group: s, element: C, text: "" + O, x: z.x, y: z.y }) } } c = m } }.bind(this)), this.eventEmitter.emit("created", { chartRect: r, svg: this.svg, options: e }) }, determineAnchorPosition: n }) }(this || global, e), e })); + +var i, l, selectedLine = null; + +/* Navigate to hash without browser history entry */ +var navigateToHash = function () { + if (window.history !== undefined && window.history.replaceState !== undefined) { + window.history.replaceState(undefined, undefined, this.getAttribute("href")); + } +}; + +var hashLinks = document.getElementsByClassName('navigatetohash'); +for (i = 0, l = hashLinks.length; i < l; i++) { + hashLinks[i].addEventListener('click', navigateToHash); +} + +/* Switch test method */ +var switchTestMethod = function () { + var method = this.getAttribute("value"); + console.log("Selected test method: " + method); + + var lines, i, l, coverageData, lineAnalysis, cells; + + lines = document.querySelectorAll('.lineAnalysis tr'); + + for (i = 1, l = lines.length; i < l; i++) { + coverageData = JSON.parse(lines[i].getAttribute('data-coverage').replace(/'/g, '"')); + lineAnalysis = coverageData[method]; + cells = lines[i].querySelectorAll('td'); + if (lineAnalysis === undefined) { + lineAnalysis = coverageData.AllTestMethods; + if (lineAnalysis.LVS !== 'gray') { + cells[0].setAttribute('class', 'red'); + cells[1].innerText = cells[1].textContent = '0'; + cells[4].setAttribute('class', 'lightred'); + } + } else { + cells[0].setAttribute('class', lineAnalysis.LVS); + cells[1].innerText = cells[1].textContent = lineAnalysis.VC; + cells[4].setAttribute('class', 'light' + lineAnalysis.LVS); + } + } +}; + +var testMethods = document.getElementsByClassName('switchtestmethod'); +for (i = 0, l = testMethods.length; i < l; i++) { + testMethods[i].addEventListener('change', switchTestMethod); +} + +/* Highlight test method by line */ +var toggleLine = function () { + if (selectedLine === this) { + selectedLine = null; + } else { + selectedLine = null; + unhighlightTestMethods(); + highlightTestMethods.call(this); + selectedLine = this; + } + +}; +var highlightTestMethods = function () { + if (selectedLine !== null) { + return; + } + + var lineAnalysis; + var coverageData = JSON.parse(this.getAttribute('data-coverage').replace(/'/g, '"')); + var testMethods = document.getElementsByClassName('testmethod'); + + for (i = 0, l = testMethods.length; i < l; i++) { + lineAnalysis = coverageData[testMethods[i].id]; + if (lineAnalysis === undefined) { + testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); + } else { + testMethods[i].className += ' light' + lineAnalysis.LVS; + } + } +}; +var unhighlightTestMethods = function () { + if (selectedLine !== null) { + return; + } + + var testMethods = document.getElementsByClassName('testmethod'); + for (i = 0, l = testMethods.length; i < l; i++) { + testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); + } +}; +var coverableLines = document.getElementsByClassName('coverableline'); +for (i = 0, l = coverableLines.length; i < l; i++) { + coverableLines[i].addEventListener('click', toggleLine); + coverableLines[i].addEventListener('mouseenter', highlightTestMethods); + coverableLines[i].addEventListener('mouseleave', unhighlightTestMethods); +} + +/* History charts */ +var renderChart = function (chart) { + // Remove current children (e.g. PNG placeholder) + while (chart.firstChild) { + chart.firstChild.remove(); + } + + var chartData = window[chart.getAttribute('data-data')]; + var options = { + axisY: { + type: undefined, + onlyInteger: true + }, + lineSmooth: false, + low: 0, + high: 100, + scaleMinSpace: 20, + onlyInteger: true, + fullWidth: true + }; + var lineChart = new Chartist.Line(chart, { + labels: [], + series: chartData.series + }, options); + + /* Zoom */ + var zoomButtonDiv = document.createElement("div"); + zoomButtonDiv.className = "toggleZoom"; + var zoomButtonLink = document.createElement("a"); + zoomButtonLink.setAttribute("href", ""); + var zoomButtonText = document.createElement("i"); + zoomButtonText.className = "icon-search-plus"; + + zoomButtonLink.appendChild(zoomButtonText); + zoomButtonDiv.appendChild(zoomButtonLink); + + chart.appendChild(zoomButtonDiv); + + zoomButtonDiv.addEventListener('click', function (event) { + event.preventDefault(); + + if (options.axisY.type === undefined) { + options.axisY.type = Chartist.AutoScaleAxis; + zoomButtonText.className = "icon-search-minus"; + } else { + options.axisY.type = undefined; + zoomButtonText.className = "icon-search-plus"; + } + + lineChart.update(null, options); + }); + + var tooltip = document.createElement("div"); + tooltip.className = "tooltip"; + + chart.appendChild(tooltip); + + /* Tooltips */ + var showToolTip = function () { + var index = this.getAttribute('ct:meta'); + + tooltip.innerHTML = chartData.tooltips[index]; + tooltip.style.display = 'block'; + }; + + var moveToolTip = function (event) { + var box = chart.getBoundingClientRect(); + var left = event.pageX - box.left - window.pageXOffset; + var top = event.pageY - box.top - window.pageYOffset; + + left = left + 20; + top = top - tooltip.offsetHeight / 2; + + if (left + tooltip.offsetWidth > box.width) { + left -= tooltip.offsetWidth + 40; + } + + if (top < 0) { + top = 0; + } + + if (top + tooltip.offsetHeight > box.height) { + top = box.height - tooltip.offsetHeight; + } + + tooltip.style.left = left + 'px'; + tooltip.style.top = top + 'px'; + }; + + var hideToolTip = function () { + tooltip.style.display = 'none'; + }; + chart.addEventListener('mousemove', moveToolTip); + + lineChart.on('created', function () { + var chartPoints = chart.getElementsByClassName('ct-point'); + for (i = 0, l = chartPoints.length; i < l; i++) { + chartPoints[i].addEventListener('mousemove', showToolTip); + chartPoints[i].addEventListener('mouseout', hideToolTip); + } + }); +}; + +var charts = document.getElementsByClassName('historychart'); +for (i = 0, l = charts.length; i < l; i++) { + renderChart(charts[i]); +} \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_cog.svg b/jobs/Backend/coveragereport/icon_cog.svg new file mode 100644 index 0000000000..d730bf1266 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_cog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_cog_dark.svg b/jobs/Backend/coveragereport/icon_cog_dark.svg new file mode 100644 index 0000000000..ccbcd9b00b --- /dev/null +++ b/jobs/Backend/coveragereport/icon_cog_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_cube.svg b/jobs/Backend/coveragereport/icon_cube.svg new file mode 100644 index 0000000000..3302443cc6 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_cube.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_cube_dark.svg b/jobs/Backend/coveragereport/icon_cube_dark.svg new file mode 100644 index 0000000000..3e7f0fa882 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_cube_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_fork.svg b/jobs/Backend/coveragereport/icon_fork.svg new file mode 100644 index 0000000000..f0148b3a36 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_fork.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_fork_dark.svg b/jobs/Backend/coveragereport/icon_fork_dark.svg new file mode 100644 index 0000000000..11930c9b67 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_fork_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_info-circled.svg b/jobs/Backend/coveragereport/icon_info-circled.svg new file mode 100644 index 0000000000..252166bba9 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_info-circled.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_info-circled_dark.svg b/jobs/Backend/coveragereport/icon_info-circled_dark.svg new file mode 100644 index 0000000000..252166bba9 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_info-circled_dark.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_minus.svg b/jobs/Backend/coveragereport/icon_minus.svg new file mode 100644 index 0000000000..3c30c365e6 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_minus.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_minus_dark.svg b/jobs/Backend/coveragereport/icon_minus_dark.svg new file mode 100644 index 0000000000..2516b6fc5c --- /dev/null +++ b/jobs/Backend/coveragereport/icon_minus_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_plus.svg b/jobs/Backend/coveragereport/icon_plus.svg new file mode 100644 index 0000000000..79327232c6 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_plus.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_plus_dark.svg b/jobs/Backend/coveragereport/icon_plus_dark.svg new file mode 100644 index 0000000000..6ed4edd0a4 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_plus_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_search-minus.svg b/jobs/Backend/coveragereport/icon_search-minus.svg new file mode 100644 index 0000000000..c174eb5e14 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_search-minus.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_search-minus_dark.svg b/jobs/Backend/coveragereport/icon_search-minus_dark.svg new file mode 100644 index 0000000000..9caaffbc36 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_search-minus_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_search-plus.svg b/jobs/Backend/coveragereport/icon_search-plus.svg new file mode 100644 index 0000000000..04b24ecc3b --- /dev/null +++ b/jobs/Backend/coveragereport/icon_search-plus.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_search-plus_dark.svg b/jobs/Backend/coveragereport/icon_search-plus_dark.svg new file mode 100644 index 0000000000..53241945e5 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_search-plus_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_sponsor.svg b/jobs/Backend/coveragereport/icon_sponsor.svg new file mode 100644 index 0000000000..bf6d959139 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_sponsor.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_star.svg b/jobs/Backend/coveragereport/icon_star.svg new file mode 100644 index 0000000000..b23c54ea0f --- /dev/null +++ b/jobs/Backend/coveragereport/icon_star.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_star_dark.svg b/jobs/Backend/coveragereport/icon_star_dark.svg new file mode 100644 index 0000000000..49c0d03431 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_star_dark.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_up-dir.svg b/jobs/Backend/coveragereport/icon_up-dir.svg new file mode 100644 index 0000000000..567c11f3e2 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_up-dir.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_up-dir_active.svg b/jobs/Backend/coveragereport/icon_up-dir_active.svg new file mode 100644 index 0000000000..bb225544a8 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_up-dir_active.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_up-down-dir.svg b/jobs/Backend/coveragereport/icon_up-down-dir.svg new file mode 100644 index 0000000000..62a3f9ccd9 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_up-down-dir.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_up-down-dir_dark.svg b/jobs/Backend/coveragereport/icon_up-down-dir_dark.svg new file mode 100644 index 0000000000..2820a250a0 --- /dev/null +++ b/jobs/Backend/coveragereport/icon_up-down-dir_dark.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_wrench.svg b/jobs/Backend/coveragereport/icon_wrench.svg new file mode 100644 index 0000000000..b6aa318cef --- /dev/null +++ b/jobs/Backend/coveragereport/icon_wrench.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/icon_wrench_dark.svg b/jobs/Backend/coveragereport/icon_wrench_dark.svg new file mode 100644 index 0000000000..5c77a9c87a --- /dev/null +++ b/jobs/Backend/coveragereport/icon_wrench_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jobs/Backend/coveragereport/index.htm b/jobs/Backend/coveragereport/index.htm new file mode 100644 index 0000000000..b5926d239e --- /dev/null +++ b/jobs/Backend/coveragereport/index.htm @@ -0,0 +1,254 @@ + + + + + + + +Summary - Coverage Report + +
+

SummaryStarSponsor

+
+
+
Information
+
+
+ + + + + + + + + + + + + + + + + + + + + +
Parser:Cobertura
Assemblies:5
Classes:12
Files:11
Coverage date:16/02/2026 - 11:45:19
+
+
+
+
+
Line coverage
+
+
31%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:188
Uncovered lines:404
Coverable lines:592
Total lines:1117
Line coverage:31.7%
+
+
+
+
+
Branch coverage
+
+
12%
+
+ + + + + + + + + + + + + +
Covered branches:32
Total branches:248
Branch coverage:12.9%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Risk Hotspots

+ +
+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AssemblyClassMethodCrap Score Cyclomatic complexity
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedTransformAsync(...)893094
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedTransformAsync(...)119034
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedGetTypeDocId(...)81228
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedCreateDocumentationId(...)34218
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedCreateDocumentationId(...)11010
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedNormalizeDocId(...)426
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedUnwrapOpenApiParameter(...)426
+
+
+

Coverage

+ +
+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + +
Line coverageBranch coverage
NameCoveredUncoveredCoverableTotalPercentageCoveredTotalPercentage
ExchangeRateUpdater.Abstraction34034103100%
 
91090%
  
ExchangeRateUpdater.Abstraction.Model.Currency1801848100%
 
7887.5%
  
ExchangeRateUpdater.Abstraction.Model.ExchangeRate1601655100%
 
22100%
 
ExchangeRateUpdater.Api5139945077111.3%
  
42141.8%
  
ExchangeRateUpdater.Api.Controllers.ExchangeRateController1301331100%
 
22100%
 
ExchangeRateUpdater.Api.Middleware.ExceptionHandlingMiddleware1218307040%
  
040%
 
Microsoft.AspNetCore.OpenApi.Generated03743745920%
 
02060%
 
Program264305586.6%
  
22100%
 
System.Runtime.CompilerServices033230%
 
00
 
ExchangeRateUpdater.CbnApiClient4614712197.8%
  
101283.3%
  
ExchangeRateUpdater.CbnApiClient.Implementation.ExchangeRateCnbApiClient311326896.8%
  
81080%
  
ExchangeRateUpdater.CbnApiClient.Mapper.CnbExchangeRateMapperExtensions1501553100%
 
22100%
 
ExchangeRateUpdater.DependencyInjection111123791.6%
  
00
 
ExchangeRateUpdater.DependencyInjection.ExchangeRateCnbServicesExtension111123791.6%
  
00
 
ExchangeRateUpdater.Service4634910893.8%
  
91275%
  
ExchangeRateUpdater.Service.ExchangeRateProvider253285989.2%
  
5862.5%
  
ExchangeRateUpdater.Service.ExchangeRateService2102149100%
 
44100%
 
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/index.html b/jobs/Backend/coveragereport/index.html new file mode 100644 index 0000000000..b5926d239e --- /dev/null +++ b/jobs/Backend/coveragereport/index.html @@ -0,0 +1,254 @@ + + + + + + + +Summary - Coverage Report + +
+

SummaryStarSponsor

+
+
+
Information
+
+
+ + + + + + + + + + + + + + + + + + + + + +
Parser:Cobertura
Assemblies:5
Classes:12
Files:11
Coverage date:16/02/2026 - 11:45:19
+
+
+
+
+
Line coverage
+
+
31%
+
+ + + + + + + + + + + + + + + + + + + + + +
Covered lines:188
Uncovered lines:404
Coverable lines:592
Total lines:1117
Line coverage:31.7%
+
+
+
+
+
Branch coverage
+
+
12%
+
+ + + + + + + + + + + + + +
Covered branches:32
Total branches:248
Branch coverage:12.9%
+
+
+
+
+
Method coverage
+
+
+

Feature is only available for sponsors

+Upgrade to PRO version +
+
+
+
+

Risk Hotspots

+ +
+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AssemblyClassMethodCrap Score Cyclomatic complexity
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedTransformAsync(...)893094
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedTransformAsync(...)119034
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedGetTypeDocId(...)81228
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedCreateDocumentationId(...)34218
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedCreateDocumentationId(...)11010
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedNormalizeDocId(...)426
ExchangeRateUpdater.ApiMicrosoft.AspNetCore.OpenApi.GeneratedUnwrapOpenApiParameter(...)426
+
+
+

Coverage

+ +
+ +++++++++++++ + + + + + + + + + + + + + + + + + + + + + + +
Line coverageBranch coverage
NameCoveredUncoveredCoverableTotalPercentageCoveredTotalPercentage
ExchangeRateUpdater.Abstraction34034103100%
 
91090%
  
ExchangeRateUpdater.Abstraction.Model.Currency1801848100%
 
7887.5%
  
ExchangeRateUpdater.Abstraction.Model.ExchangeRate1601655100%
 
22100%
 
ExchangeRateUpdater.Api5139945077111.3%
  
42141.8%
  
ExchangeRateUpdater.Api.Controllers.ExchangeRateController1301331100%
 
22100%
 
ExchangeRateUpdater.Api.Middleware.ExceptionHandlingMiddleware1218307040%
  
040%
 
Microsoft.AspNetCore.OpenApi.Generated03743745920%
 
02060%
 
Program264305586.6%
  
22100%
 
System.Runtime.CompilerServices033230%
 
00
 
ExchangeRateUpdater.CbnApiClient4614712197.8%
  
101283.3%
  
ExchangeRateUpdater.CbnApiClient.Implementation.ExchangeRateCnbApiClient311326896.8%
  
81080%
  
ExchangeRateUpdater.CbnApiClient.Mapper.CnbExchangeRateMapperExtensions1501553100%
 
22100%
 
ExchangeRateUpdater.DependencyInjection111123791.6%
  
00
 
ExchangeRateUpdater.DependencyInjection.ExchangeRateCnbServicesExtension111123791.6%
  
00
 
ExchangeRateUpdater.Service4634910893.8%
  
91275%
  
ExchangeRateUpdater.Service.ExchangeRateProvider253285989.2%
  
5862.5%
  
ExchangeRateUpdater.Service.ExchangeRateService2102149100%
 
44100%
 
+
+
+
+ \ No newline at end of file diff --git a/jobs/Backend/coveragereport/main.js b/jobs/Backend/coveragereport/main.js new file mode 100644 index 0000000000..36a864dd2f --- /dev/null +++ b/jobs/Backend/coveragereport/main.js @@ -0,0 +1,356 @@ +/* Chartist.js 0.11.4 + * Copyright © 2019 Gion Kunz + * Free to use under either the WTFPL license or the MIT license. + * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL + * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT + */ + +!function (e, t) { "function" == typeof define && define.amd ? define("Chartist", [], (function () { return e.Chartist = t() })) : "object" == typeof module && module.exports ? module.exports = t() : e.Chartist = t() }(this, (function () { var e = { version: "0.11.4" }; return function (e, t) { "use strict"; var i = e.window, n = e.document; t.namespaces = { svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns/", xhtml: "http://www.w3.org/1999/xhtml", xlink: "http://www.w3.org/1999/xlink", ct: "http://gionkunz.github.com/chartist-js/ct" }, t.noop = function (e) { return e }, t.alphaNumerate = function (e) { return String.fromCharCode(97 + e % 26) }, t.extend = function (e) { var i, n, s, r; for (e = e || {}, i = 1; i < arguments.length; i++)for (var a in n = arguments[i], r = Object.getPrototypeOf(e), n) "__proto__" === a || "constructor" === a || null !== r && a in r || (s = n[a], e[a] = "object" != typeof s || null === s || s instanceof Array ? s : t.extend(e[a], s)); return e }, t.replaceAll = function (e, t, i) { return e.replace(new RegExp(t, "g"), i) }, t.ensureUnit = function (e, t) { return "number" == typeof e && (e += t), e }, t.quantity = function (e) { if ("string" == typeof e) { var t = /^(\d+)\s*(.*)$/g.exec(e); return { value: +t[1], unit: t[2] || void 0 } } return { value: e } }, t.querySelector = function (e) { return e instanceof Node ? e : n.querySelector(e) }, t.times = function (e) { return Array.apply(null, new Array(e)) }, t.sum = function (e, t) { return e + (t || 0) }, t.mapMultiply = function (e) { return function (t) { return t * e } }, t.mapAdd = function (e) { return function (t) { return t + e } }, t.serialMap = function (e, i) { var n = [], s = Math.max.apply(null, e.map((function (e) { return e.length }))); return t.times(s).forEach((function (t, s) { var r = e.map((function (e) { return e[s] })); n[s] = i.apply(null, r) })), n }, t.roundWithPrecision = function (e, i) { var n = Math.pow(10, i || t.precision); return Math.round(e * n) / n }, t.precision = 8, t.escapingMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, t.serialize = function (e) { return null == e ? e : ("number" == typeof e ? e = "" + e : "object" == typeof e && (e = JSON.stringify({ data: e })), Object.keys(t.escapingMap).reduce((function (e, i) { return t.replaceAll(e, i, t.escapingMap[i]) }), e)) }, t.deserialize = function (e) { if ("string" != typeof e) return e; e = Object.keys(t.escapingMap).reduce((function (e, i) { return t.replaceAll(e, t.escapingMap[i], i) }), e); try { e = void 0 !== (e = JSON.parse(e)).data ? e.data : e } catch (e) { } return e }, t.createSvg = function (e, i, n, s) { var r; return i = i || "100%", n = n || "100%", Array.prototype.slice.call(e.querySelectorAll("svg")).filter((function (e) { return e.getAttributeNS(t.namespaces.xmlns, "ct") })).forEach((function (t) { e.removeChild(t) })), (r = new t.Svg("svg").attr({ width: i, height: n }).addClass(s))._node.style.width = i, r._node.style.height = n, e.appendChild(r._node), r }, t.normalizeData = function (e, i, n) { var s, r = { raw: e, normalized: {} }; return r.normalized.series = t.getDataArray({ series: e.series || [] }, i, n), s = r.normalized.series.every((function (e) { return e instanceof Array })) ? Math.max.apply(null, r.normalized.series.map((function (e) { return e.length }))) : r.normalized.series.length, r.normalized.labels = (e.labels || []).slice(), Array.prototype.push.apply(r.normalized.labels, t.times(Math.max(0, s - r.normalized.labels.length)).map((function () { return "" }))), i && t.reverseData(r.normalized), r }, t.safeHasProperty = function (e, t) { return null !== e && "object" == typeof e && e.hasOwnProperty(t) }, t.isDataHoleValue = function (e) { return null == e || "number" == typeof e && isNaN(e) }, t.reverseData = function (e) { e.labels.reverse(), e.series.reverse(); for (var t = 0; t < e.series.length; t++)"object" == typeof e.series[t] && void 0 !== e.series[t].data ? e.series[t].data.reverse() : e.series[t] instanceof Array && e.series[t].reverse() }, t.getDataArray = function (e, i, n) { return e.series.map((function e(i) { if (t.safeHasProperty(i, "value")) return e(i.value); if (t.safeHasProperty(i, "data")) return e(i.data); if (i instanceof Array) return i.map(e); if (!t.isDataHoleValue(i)) { if (n) { var s = {}; return "string" == typeof n ? s[n] = t.getNumberOrUndefined(i) : s.y = t.getNumberOrUndefined(i), s.x = i.hasOwnProperty("x") ? t.getNumberOrUndefined(i.x) : s.x, s.y = i.hasOwnProperty("y") ? t.getNumberOrUndefined(i.y) : s.y, s } return t.getNumberOrUndefined(i) } })) }, t.normalizePadding = function (e, t) { return t = t || 0, "number" == typeof e ? { top: e, right: e, bottom: e, left: e } : { top: "number" == typeof e.top ? e.top : t, right: "number" == typeof e.right ? e.right : t, bottom: "number" == typeof e.bottom ? e.bottom : t, left: "number" == typeof e.left ? e.left : t } }, t.getMetaData = function (e, t) { var i = e.data ? e.data[t] : e[t]; return i ? i.meta : void 0 }, t.orderOfMagnitude = function (e) { return Math.floor(Math.log(Math.abs(e)) / Math.LN10) }, t.projectLength = function (e, t, i) { return t / i.range * e }, t.getAvailableHeight = function (e, i) { return Math.max((t.quantity(i.height).value || e.height()) - (i.chartPadding.top + i.chartPadding.bottom) - i.axisX.offset, 0) }, t.getHighLow = function (e, i, n) { var s = { high: void 0 === (i = t.extend({}, i, n ? i["axis" + n.toUpperCase()] : {})).high ? -Number.MAX_VALUE : +i.high, low: void 0 === i.low ? Number.MAX_VALUE : +i.low }, r = void 0 === i.high, a = void 0 === i.low; return (r || a) && function e(t) { if (void 0 !== t) if (t instanceof Array) for (var i = 0; i < t.length; i++)e(t[i]); else { var o = n ? +t[n] : +t; r && o > s.high && (s.high = o), a && o < s.low && (s.low = o) } }(e), (i.referenceValue || 0 === i.referenceValue) && (s.high = Math.max(i.referenceValue, s.high), s.low = Math.min(i.referenceValue, s.low)), s.high <= s.low && (0 === s.low ? s.high = 1 : s.low < 0 ? s.high = 0 : (s.high > 0 || (s.high = 1), s.low = 0)), s }, t.isNumeric = function (e) { return null !== e && isFinite(e) }, t.isFalseyButZero = function (e) { return !e && 0 !== e }, t.getNumberOrUndefined = function (e) { return t.isNumeric(e) ? +e : void 0 }, t.isMultiValue = function (e) { return "object" == typeof e && ("x" in e || "y" in e) }, t.getMultiValue = function (e, i) { return t.isMultiValue(e) ? t.getNumberOrUndefined(e[i || "y"]) : t.getNumberOrUndefined(e) }, t.rho = function (e) { if (1 === e) return e; function t(e, i) { return e % i == 0 ? i : t(i, e % i) } function i(e) { return e * e + 1 } var n, s = 2, r = 2; if (e % 2 == 0) return 2; do { s = i(s) % e, r = i(i(r)) % e, n = t(Math.abs(s - r), e) } while (1 === n); return n }, t.getBounds = function (e, i, n, s) { var r, a, o, l = 0, h = { high: i.high, low: i.low }; h.valueRange = h.high - h.low, h.oom = t.orderOfMagnitude(h.valueRange), h.step = Math.pow(10, h.oom), h.min = Math.floor(h.low / h.step) * h.step, h.max = Math.ceil(h.high / h.step) * h.step, h.range = h.max - h.min, h.numberOfSteps = Math.round(h.range / h.step); var u = t.projectLength(e, h.step, h) < n, c = s ? t.rho(h.range) : 0; if (s && t.projectLength(e, 1, h) >= n) h.step = 1; else if (s && c < h.step && t.projectLength(e, c, h) >= n) h.step = c; else for (; ;) { if (u && t.projectLength(e, h.step, h) <= n) h.step *= 2; else { if (u || !(t.projectLength(e, h.step / 2, h) >= n)) break; if (h.step /= 2, s && h.step % 1 != 0) { h.step *= 2; break } } if (l++ > 1e3) throw new Error("Exceeded maximum number of iterations while optimizing scale step!") } var d = 2221e-19; function p(e, t) { return e === (e += t) && (e *= 1 + (t > 0 ? d : -d)), e } for (h.step = Math.max(h.step, d), a = h.min, o = h.max; a + h.step <= h.low;)a = p(a, h.step); for (; o - h.step >= h.high;)o = p(o, -h.step); h.min = a, h.max = o, h.range = h.max - h.min; var f = []; for (r = h.min; r <= h.max; r = p(r, h.step)) { var m = t.roundWithPrecision(r); m !== f[f.length - 1] && f.push(m) } return h.values = f, h }, t.polarToCartesian = function (e, t, i, n) { var s = (n - 90) * Math.PI / 180; return { x: e + i * Math.cos(s), y: t + i * Math.sin(s) } }, t.createChartRect = function (e, i, n) { var s = !(!i.axisX && !i.axisY), r = s ? i.axisY.offset : 0, a = s ? i.axisX.offset : 0, o = e.width() || t.quantity(i.width).value || 0, l = e.height() || t.quantity(i.height).value || 0, h = t.normalizePadding(i.chartPadding, n); o = Math.max(o, r + h.left + h.right), l = Math.max(l, a + h.top + h.bottom); var u = { padding: h, width: function () { return this.x2 - this.x1 }, height: function () { return this.y1 - this.y2 } }; return s ? ("start" === i.axisX.position ? (u.y2 = h.top + a, u.y1 = Math.max(l - h.bottom, u.y2 + 1)) : (u.y2 = h.top, u.y1 = Math.max(l - h.bottom - a, u.y2 + 1)), "start" === i.axisY.position ? (u.x1 = h.left + r, u.x2 = Math.max(o - h.right, u.x1 + 1)) : (u.x1 = h.left, u.x2 = Math.max(o - h.right - r, u.x1 + 1))) : (u.x1 = h.left, u.x2 = Math.max(o - h.right, u.x1 + 1), u.y2 = h.top, u.y1 = Math.max(l - h.bottom, u.y2 + 1)), u }, t.createGrid = function (e, i, n, s, r, a, o, l) { var h = {}; h[n.units.pos + "1"] = e, h[n.units.pos + "2"] = e, h[n.counterUnits.pos + "1"] = s, h[n.counterUnits.pos + "2"] = s + r; var u = a.elem("line", h, o.join(" ")); l.emit("draw", t.extend({ type: "grid", axis: n, index: i, group: a, element: u }, h)) }, t.createGridBackground = function (e, t, i, n) { var s = e.elem("rect", { x: t.x1, y: t.y2, width: t.width(), height: t.height() }, i, !0); n.emit("draw", { type: "gridBackground", group: e, element: s }) }, t.createLabel = function (e, i, s, r, a, o, l, h, u, c, d) { var p, f = {}; if (f[a.units.pos] = e + l[a.units.pos], f[a.counterUnits.pos] = l[a.counterUnits.pos], f[a.units.len] = i, f[a.counterUnits.len] = Math.max(0, o - 10), c) { var m = n.createElement("span"); m.className = u.join(" "), m.setAttribute("xmlns", t.namespaces.xhtml), m.innerText = r[s], m.style[a.units.len] = Math.round(f[a.units.len]) + "px", m.style[a.counterUnits.len] = Math.round(f[a.counterUnits.len]) + "px", p = h.foreignObject(m, t.extend({ style: "overflow: visible;" }, f)) } else p = h.elem("text", f, u.join(" ")).text(r[s]); d.emit("draw", t.extend({ type: "label", axis: a, index: s, group: h, element: p, text: r[s] }, f)) }, t.getSeriesOption = function (e, t, i) { if (e.name && t.series && t.series[e.name]) { var n = t.series[e.name]; return n.hasOwnProperty(i) ? n[i] : t[i] } return t[i] }, t.optionsProvider = function (e, n, s) { var r, a, o = t.extend({}, e), l = []; function h(e) { var l = r; if (r = t.extend({}, o), n) for (a = 0; a < n.length; a++) { i.matchMedia(n[a][0]).matches && (r = t.extend(r, n[a][1])) } s && e && s.emit("optionsChanged", { previousOptions: l, currentOptions: r }) } if (!i.matchMedia) throw "window.matchMedia not found! Make sure you're using a polyfill."; if (n) for (a = 0; a < n.length; a++) { var u = i.matchMedia(n[a][0]); u.addListener(h), l.push(u) } return h(), { removeMediaQueryListeners: function () { l.forEach((function (e) { e.removeListener(h) })) }, getCurrentOptions: function () { return t.extend({}, r) } } }, t.splitIntoSegments = function (e, i, n) { n = t.extend({}, { increasingX: !1, fillHoles: !1 }, n); for (var s = [], r = !0, a = 0; a < e.length; a += 2)void 0 === t.getMultiValue(i[a / 2].value) ? n.fillHoles || (r = !0) : (n.increasingX && a >= 2 && e[a] <= e[a - 2] && (r = !0), r && (s.push({ pathCoordinates: [], valueData: [] }), r = !1), s[s.length - 1].pathCoordinates.push(e[a], e[a + 1]), s[s.length - 1].valueData.push(i[a / 2])); return s } }(this || global, e), function (e, t) { "use strict"; t.Interpolation = {}, t.Interpolation.none = function (e) { return e = t.extend({}, { fillHoles: !1 }, e), function (i, n) { for (var s = new t.Svg.Path, r = !0, a = 0; a < i.length; a += 2) { var o = i[a], l = i[a + 1], h = n[a / 2]; void 0 !== t.getMultiValue(h.value) ? (r ? s.move(o, l, !1, h) : s.line(o, l, !1, h), r = !1) : e.fillHoles || (r = !0) } return s } }, t.Interpolation.simple = function (e) { e = t.extend({}, { divisor: 2, fillHoles: !1 }, e); var i = 1 / Math.max(1, e.divisor); return function (n, s) { for (var r, a, o, l = new t.Svg.Path, h = 0; h < n.length; h += 2) { var u = n[h], c = n[h + 1], d = (u - r) * i, p = s[h / 2]; void 0 !== p.value ? (void 0 === o ? l.move(u, c, !1, p) : l.curve(r + d, a, u - d, c, u, c, !1, p), r = u, a = c, o = p) : e.fillHoles || (r = u = o = void 0) } return l } }, t.Interpolation.cardinal = function (e) { e = t.extend({}, { tension: 1, fillHoles: !1 }, e); var i = Math.min(1, Math.max(0, e.tension)), n = 1 - i; return function s(r, a) { var o = t.splitIntoSegments(r, a, { fillHoles: e.fillHoles }); if (o.length) { if (o.length > 1) { var l = []; return o.forEach((function (e) { l.push(s(e.pathCoordinates, e.valueData)) })), t.Svg.Path.join(l) } if (r = o[0].pathCoordinates, a = o[0].valueData, r.length <= 4) return t.Interpolation.none()(r, a); for (var h = (new t.Svg.Path).move(r[0], r[1], !1, a[0]), u = 0, c = r.length; c - 2 > u; u += 2) { var d = [{ x: +r[u - 2], y: +r[u - 1] }, { x: +r[u], y: +r[u + 1] }, { x: +r[u + 2], y: +r[u + 3] }, { x: +r[u + 4], y: +r[u + 5] }]; c - 4 === u ? d[3] = d[2] : u || (d[0] = { x: +r[u], y: +r[u + 1] }), h.curve(i * (-d[0].x + 6 * d[1].x + d[2].x) / 6 + n * d[2].x, i * (-d[0].y + 6 * d[1].y + d[2].y) / 6 + n * d[2].y, i * (d[1].x + 6 * d[2].x - d[3].x) / 6 + n * d[2].x, i * (d[1].y + 6 * d[2].y - d[3].y) / 6 + n * d[2].y, d[2].x, d[2].y, !1, a[(u + 2) / 2]) } return h } return t.Interpolation.none()([]) } }, t.Interpolation.monotoneCubic = function (e) { return e = t.extend({}, { fillHoles: !1 }, e), function i(n, s) { var r = t.splitIntoSegments(n, s, { fillHoles: e.fillHoles, increasingX: !0 }); if (r.length) { if (r.length > 1) { var a = []; return r.forEach((function (e) { a.push(i(e.pathCoordinates, e.valueData)) })), t.Svg.Path.join(a) } if (n = r[0].pathCoordinates, s = r[0].valueData, n.length <= 4) return t.Interpolation.none()(n, s); var o, l, h = [], u = [], c = n.length / 2, d = [], p = [], f = [], m = []; for (o = 0; o < c; o++)h[o] = n[2 * o], u[o] = n[2 * o + 1]; for (o = 0; o < c - 1; o++)f[o] = u[o + 1] - u[o], m[o] = h[o + 1] - h[o], p[o] = f[o] / m[o]; for (d[0] = p[0], d[c - 1] = p[c - 2], o = 1; o < c - 1; o++)0 === p[o] || 0 === p[o - 1] || p[o - 1] > 0 != p[o] > 0 ? d[o] = 0 : (d[o] = 3 * (m[o - 1] + m[o]) / ((2 * m[o] + m[o - 1]) / p[o - 1] + (m[o] + 2 * m[o - 1]) / p[o]), isFinite(d[o]) || (d[o] = 0)); for (l = (new t.Svg.Path).move(h[0], u[0], !1, s[0]), o = 0; o < c - 1; o++)l.curve(h[o] + m[o] / 3, u[o] + d[o] * m[o] / 3, h[o + 1] - m[o] / 3, u[o + 1] - d[o + 1] * m[o] / 3, h[o + 1], u[o + 1], !1, s[o + 1]); return l } return t.Interpolation.none()([]) } }, t.Interpolation.step = function (e) { return e = t.extend({}, { postpone: !0, fillHoles: !1 }, e), function (i, n) { for (var s, r, a, o = new t.Svg.Path, l = 0; l < i.length; l += 2) { var h = i[l], u = i[l + 1], c = n[l / 2]; void 0 !== c.value ? (void 0 === a ? o.move(h, u, !1, c) : (e.postpone ? o.line(h, r, !1, a) : o.line(s, u, !1, c), o.line(h, u, !1, c)), s = h, r = u, a = c) : e.fillHoles || (s = r = a = void 0) } return o } } }(this || global, e), function (e, t) { "use strict"; t.EventEmitter = function () { var e = []; return { addEventHandler: function (t, i) { e[t] = e[t] || [], e[t].push(i) }, removeEventHandler: function (t, i) { e[t] && (i ? (e[t].splice(e[t].indexOf(i), 1), 0 === e[t].length && delete e[t]) : delete e[t]) }, emit: function (t, i) { e[t] && e[t].forEach((function (e) { e(i) })), e["*"] && e["*"].forEach((function (e) { e(t, i) })) } } } }(this || global, e), function (e, t) { "use strict"; t.Class = { extend: function (e, i) { var n = i || this.prototype || t.Class, s = Object.create(n); t.Class.cloneDefinitions(s, e); var r = function () { var e, i = s.constructor || function () { }; return e = this === t ? Object.create(s) : this, i.apply(e, Array.prototype.slice.call(arguments, 0)), e }; return r.prototype = s, r.super = n, r.extend = this.extend, r }, cloneDefinitions: function () { var e = function (e) { var t = []; if (e.length) for (var i = 0; i < e.length; i++)t.push(e[i]); return t }(arguments), t = e[0]; return e.splice(1, e.length - 1).forEach((function (e) { Object.getOwnPropertyNames(e).forEach((function (i) { delete t[i], Object.defineProperty(t, i, Object.getOwnPropertyDescriptor(e, i)) })) })), t } } }(this || global, e), function (e, t) { "use strict"; var i = e.window; function n() { i.addEventListener("resize", this.resizeListener), this.optionsProvider = t.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter), this.eventEmitter.addEventHandler("optionsChanged", function () { this.update() }.bind(this)), this.options.plugins && this.options.plugins.forEach(function (e) { e instanceof Array ? e[0](this, e[1]) : e(this) }.bind(this)), this.eventEmitter.emit("data", { type: "initial", data: this.data }), this.createChart(this.optionsProvider.getCurrentOptions()), this.initializeTimeoutId = void 0 } t.Base = t.Class.extend({ constructor: function (e, i, s, r, a) { this.container = t.querySelector(e), this.data = i || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.defaultOptions = s, this.options = r, this.responsiveOptions = a, this.eventEmitter = t.EventEmitter(), this.supportsForeignObject = t.Svg.isSupported("Extensibility"), this.supportsAnimations = t.Svg.isSupported("AnimationEventsAttribute"), this.resizeListener = function () { this.update() }.bind(this), this.container && (this.container.__chartist__ && this.container.__chartist__.detach(), this.container.__chartist__ = this), this.initializeTimeoutId = setTimeout(n.bind(this), 0) }, optionsProvider: void 0, container: void 0, svg: void 0, eventEmitter: void 0, createChart: function () { throw new Error("Base chart type can't be instantiated!") }, update: function (e, i, n) { return e && (this.data = e || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.eventEmitter.emit("data", { type: "update", data: this.data })), i && (this.options = t.extend({}, n ? this.options : this.defaultOptions, i), this.initializeTimeoutId || (this.optionsProvider.removeMediaQueryListeners(), this.optionsProvider = t.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter))), this.initializeTimeoutId || this.createChart(this.optionsProvider.getCurrentOptions()), this }, detach: function () { return this.initializeTimeoutId ? i.clearTimeout(this.initializeTimeoutId) : (i.removeEventListener("resize", this.resizeListener), this.optionsProvider.removeMediaQueryListeners()), this }, on: function (e, t) { return this.eventEmitter.addEventHandler(e, t), this }, off: function (e, t) { return this.eventEmitter.removeEventHandler(e, t), this }, version: t.version, supportsForeignObject: !1 }) }(this || global, e), function (e, t) { "use strict"; var i = e.document; t.Svg = t.Class.extend({ constructor: function (e, n, s, r, a) { e instanceof Element ? this._node = e : (this._node = i.createElementNS(t.namespaces.svg, e), "svg" === e && this.attr({ "xmlns:ct": t.namespaces.ct })), n && this.attr(n), s && this.addClass(s), r && (a && r._node.firstChild ? r._node.insertBefore(this._node, r._node.firstChild) : r._node.appendChild(this._node)) }, attr: function (e, i) { return "string" == typeof e ? i ? this._node.getAttributeNS(i, e) : this._node.getAttribute(e) : (Object.keys(e).forEach(function (i) { if (void 0 !== e[i]) if (-1 !== i.indexOf(":")) { var n = i.split(":"); this._node.setAttributeNS(t.namespaces[n[0]], i, e[i]) } else this._node.setAttribute(i, e[i]) }.bind(this)), this) }, elem: function (e, i, n, s) { return new t.Svg(e, i, n, this, s) }, parent: function () { return this._node.parentNode instanceof SVGElement ? new t.Svg(this._node.parentNode) : null }, root: function () { for (var e = this._node; "svg" !== e.nodeName;)e = e.parentNode; return new t.Svg(e) }, querySelector: function (e) { var i = this._node.querySelector(e); return i ? new t.Svg(i) : null }, querySelectorAll: function (e) { var i = this._node.querySelectorAll(e); return i.length ? new t.Svg.List(i) : null }, getNode: function () { return this._node }, foreignObject: function (e, n, s, r) { if ("string" == typeof e) { var a = i.createElement("div"); a.innerHTML = e, e = a.firstChild } e.setAttribute("xmlns", t.namespaces.xmlns); var o = this.elem("foreignObject", n, s, r); return o._node.appendChild(e), o }, text: function (e) { return this._node.appendChild(i.createTextNode(e)), this }, empty: function () { for (; this._node.firstChild;)this._node.removeChild(this._node.firstChild); return this }, remove: function () { return this._node.parentNode.removeChild(this._node), this.parent() }, replace: function (e) { return this._node.parentNode.replaceChild(e._node, this._node), e }, append: function (e, t) { return t && this._node.firstChild ? this._node.insertBefore(e._node, this._node.firstChild) : this._node.appendChild(e._node), this }, classes: function () { return this._node.getAttribute("class") ? this._node.getAttribute("class").trim().split(/\s+/) : [] }, addClass: function (e) { return this._node.setAttribute("class", this.classes(this._node).concat(e.trim().split(/\s+/)).filter((function (e, t, i) { return i.indexOf(e) === t })).join(" ")), this }, removeClass: function (e) { var t = e.trim().split(/\s+/); return this._node.setAttribute("class", this.classes(this._node).filter((function (e) { return -1 === t.indexOf(e) })).join(" ")), this }, removeAllClasses: function () { return this._node.setAttribute("class", ""), this }, height: function () { return this._node.getBoundingClientRect().height }, width: function () { return this._node.getBoundingClientRect().width }, animate: function (e, i, n) { return void 0 === i && (i = !0), Object.keys(e).forEach(function (s) { function r(e, i) { var r, a, o, l = {}; e.easing && (o = e.easing instanceof Array ? e.easing : t.Svg.Easing[e.easing], delete e.easing), e.begin = t.ensureUnit(e.begin, "ms"), e.dur = t.ensureUnit(e.dur, "ms"), o && (e.calcMode = "spline", e.keySplines = o.join(" "), e.keyTimes = "0;1"), i && (e.fill = "freeze", l[s] = e.from, this.attr(l), a = t.quantity(e.begin || 0).value, e.begin = "indefinite"), r = this.elem("animate", t.extend({ attributeName: s }, e)), i && setTimeout(function () { try { r._node.beginElement() } catch (t) { l[s] = e.to, this.attr(l), r.remove() } }.bind(this), a), n && r._node.addEventListener("beginEvent", function () { n.emit("animationBegin", { element: this, animate: r._node, params: e }) }.bind(this)), r._node.addEventListener("endEvent", function () { n && n.emit("animationEnd", { element: this, animate: r._node, params: e }), i && (l[s] = e.to, this.attr(l), r.remove()) }.bind(this)) } e[s] instanceof Array ? e[s].forEach(function (e) { r.bind(this)(e, !1) }.bind(this)) : r.bind(this)(e[s], i) }.bind(this)), this } }), t.Svg.isSupported = function (e) { return i.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#" + e, "1.1") }; t.Svg.Easing = { easeInSine: [.47, 0, .745, .715], easeOutSine: [.39, .575, .565, 1], easeInOutSine: [.445, .05, .55, .95], easeInQuad: [.55, .085, .68, .53], easeOutQuad: [.25, .46, .45, .94], easeInOutQuad: [.455, .03, .515, .955], easeInCubic: [.55, .055, .675, .19], easeOutCubic: [.215, .61, .355, 1], easeInOutCubic: [.645, .045, .355, 1], easeInQuart: [.895, .03, .685, .22], easeOutQuart: [.165, .84, .44, 1], easeInOutQuart: [.77, 0, .175, 1], easeInQuint: [.755, .05, .855, .06], easeOutQuint: [.23, 1, .32, 1], easeInOutQuint: [.86, 0, .07, 1], easeInExpo: [.95, .05, .795, .035], easeOutExpo: [.19, 1, .22, 1], easeInOutExpo: [1, 0, 0, 1], easeInCirc: [.6, .04, .98, .335], easeOutCirc: [.075, .82, .165, 1], easeInOutCirc: [.785, .135, .15, .86], easeInBack: [.6, -.28, .735, .045], easeOutBack: [.175, .885, .32, 1.275], easeInOutBack: [.68, -.55, .265, 1.55] }, t.Svg.List = t.Class.extend({ constructor: function (e) { var i = this; this.svgElements = []; for (var n = 0; n < e.length; n++)this.svgElements.push(new t.Svg(e[n])); Object.keys(t.Svg.prototype).filter((function (e) { return -1 === ["constructor", "parent", "querySelector", "querySelectorAll", "replace", "append", "classes", "height", "width"].indexOf(e) })).forEach((function (e) { i[e] = function () { var n = Array.prototype.slice.call(arguments, 0); return i.svgElements.forEach((function (i) { t.Svg.prototype[e].apply(i, n) })), i } })) } }) }(this || global, e), function (e, t) { "use strict"; var i = { m: ["x", "y"], l: ["x", "y"], c: ["x1", "y1", "x2", "y2", "x", "y"], a: ["rx", "ry", "xAr", "lAf", "sf", "x", "y"] }, n = { accuracy: 3 }; function s(e, i, n, s, r, a) { var o = t.extend({ command: r ? e.toLowerCase() : e.toUpperCase() }, i, a ? { data: a } : {}); n.splice(s, 0, o) } function r(e, t) { e.forEach((function (n, s) { i[n.command.toLowerCase()].forEach((function (i, r) { t(n, i, s, r, e) })) })) } t.Svg.Path = t.Class.extend({ constructor: function (e, i) { this.pathElements = [], this.pos = 0, this.close = e, this.options = t.extend({}, n, i) }, position: function (e) { return void 0 !== e ? (this.pos = Math.max(0, Math.min(this.pathElements.length, e)), this) : this.pos }, remove: function (e) { return this.pathElements.splice(this.pos, e), this }, move: function (e, t, i, n) { return s("M", { x: +e, y: +t }, this.pathElements, this.pos++, i, n), this }, line: function (e, t, i, n) { return s("L", { x: +e, y: +t }, this.pathElements, this.pos++, i, n), this }, curve: function (e, t, i, n, r, a, o, l) { return s("C", { x1: +e, y1: +t, x2: +i, y2: +n, x: +r, y: +a }, this.pathElements, this.pos++, o, l), this }, arc: function (e, t, i, n, r, a, o, l, h) { return s("A", { rx: +e, ry: +t, xAr: +i, lAf: +n, sf: +r, x: +a, y: +o }, this.pathElements, this.pos++, l, h), this }, scale: function (e, t) { return r(this.pathElements, (function (i, n) { i[n] *= "x" === n[0] ? e : t })), this }, translate: function (e, t) { return r(this.pathElements, (function (i, n) { i[n] += "x" === n[0] ? e : t })), this }, transform: function (e) { return r(this.pathElements, (function (t, i, n, s, r) { var a = e(t, i, n, s, r); (a || 0 === a) && (t[i] = a) })), this }, parse: function (e) { var n = e.replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/([0-9])([A-Za-z])/g, "$1 $2").split(/[\s,]+/).reduce((function (e, t) { return t.match(/[A-Za-z]/) && e.push([]), e[e.length - 1].push(t), e }), []); "Z" === n[n.length - 1][0].toUpperCase() && n.pop(); var s = n.map((function (e) { var n = e.shift(), s = i[n.toLowerCase()]; return t.extend({ command: n }, s.reduce((function (t, i, n) { return t[i] = +e[n], t }), {})) })), r = [this.pos, 0]; return Array.prototype.push.apply(r, s), Array.prototype.splice.apply(this.pathElements, r), this.pos += s.length, this }, stringify: function () { var e = Math.pow(10, this.options.accuracy); return this.pathElements.reduce(function (t, n) { var s = i[n.command.toLowerCase()].map(function (t) { return this.options.accuracy ? Math.round(n[t] * e) / e : n[t] }.bind(this)); return t + n.command + s.join(",") }.bind(this), "") + (this.close ? "Z" : "") }, clone: function (e) { var i = new t.Svg.Path(e || this.close); return i.pos = this.pos, i.pathElements = this.pathElements.slice().map((function (e) { return t.extend({}, e) })), i.options = t.extend({}, this.options), i }, splitByCommand: function (e) { var i = [new t.Svg.Path]; return this.pathElements.forEach((function (n) { n.command === e.toUpperCase() && 0 !== i[i.length - 1].pathElements.length && i.push(new t.Svg.Path), i[i.length - 1].pathElements.push(n) })), i } }), t.Svg.Path.elementDescriptions = i, t.Svg.Path.join = function (e, i, n) { for (var s = new t.Svg.Path(i, n), r = 0; r < e.length; r++)for (var a = e[r], o = 0; o < a.pathElements.length; o++)s.pathElements.push(a.pathElements[o]); return s } }(this || global, e), function (e, t) { "use strict"; e.window, e.document; var i = { x: { pos: "x", len: "width", dir: "horizontal", rectStart: "x1", rectEnd: "x2", rectOffset: "y2" }, y: { pos: "y", len: "height", dir: "vertical", rectStart: "y2", rectEnd: "y1", rectOffset: "x1" } }; t.Axis = t.Class.extend({ constructor: function (e, t, n, s) { this.units = e, this.counterUnits = e === i.x ? i.y : i.x, this.chartRect = t, this.axisLength = t[e.rectEnd] - t[e.rectStart], this.gridOffset = t[e.rectOffset], this.ticks = n, this.options = s }, createGridAndLabels: function (e, i, n, s, r) { var a = s["axis" + this.units.pos.toUpperCase()], o = this.ticks.map(this.projectValue.bind(this)), l = this.ticks.map(a.labelInterpolationFnc); o.forEach(function (h, u) { var c, d = { x: 0, y: 0 }; c = o[u + 1] ? o[u + 1] - h : Math.max(this.axisLength - h, 30), t.isFalseyButZero(l[u]) && "" !== l[u] || ("x" === this.units.pos ? (h = this.chartRect.x1 + h, d.x = s.axisX.labelOffset.x, "start" === s.axisX.position ? d.y = this.chartRect.padding.top + s.axisX.labelOffset.y + (n ? 5 : 20) : d.y = this.chartRect.y1 + s.axisX.labelOffset.y + (n ? 5 : 20)) : (h = this.chartRect.y1 - h, d.y = s.axisY.labelOffset.y - (n ? c : 0), "start" === s.axisY.position ? d.x = n ? this.chartRect.padding.left + s.axisY.labelOffset.x : this.chartRect.x1 - 10 : d.x = this.chartRect.x2 + s.axisY.labelOffset.x + 10), a.showGrid && t.createGrid(h, u, this, this.gridOffset, this.chartRect[this.counterUnits.len](), e, [s.classNames.grid, s.classNames[this.units.dir]], r), a.showLabel && t.createLabel(h, c, u, l, this, a.offset, d, i, [s.classNames.label, s.classNames[this.units.dir], "start" === a.position ? s.classNames[a.position] : s.classNames.end], n, r)) }.bind(this)) }, projectValue: function (e, t, i) { throw new Error("Base axis can't be instantiated!") } }), t.Axis.units = i }(this || global, e), function (e, t) { "use strict"; e.window, e.document; t.AutoScaleAxis = t.Axis.extend({ constructor: function (e, i, n, s) { var r = s.highLow || t.getHighLow(i, s, e.pos); this.bounds = t.getBounds(n[e.rectEnd] - n[e.rectStart], r, s.scaleMinSpace || 20, s.onlyInteger), this.range = { min: this.bounds.min, max: this.bounds.max }, t.AutoScaleAxis.super.constructor.call(this, e, n, this.bounds.values, s) }, projectValue: function (e) { return this.axisLength * (+t.getMultiValue(e, this.units.pos) - this.bounds.min) / this.bounds.range } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; t.FixedScaleAxis = t.Axis.extend({ constructor: function (e, i, n, s) { var r = s.highLow || t.getHighLow(i, s, e.pos); this.divisor = s.divisor || 1, this.ticks = s.ticks || t.times(this.divisor).map(function (e, t) { return r.low + (r.high - r.low) / this.divisor * t }.bind(this)), this.ticks.sort((function (e, t) { return e - t })), this.range = { min: r.low, max: r.high }, t.FixedScaleAxis.super.constructor.call(this, e, n, this.ticks, s), this.stepLength = this.axisLength / this.divisor }, projectValue: function (e) { return this.axisLength * (+t.getMultiValue(e, this.units.pos) - this.range.min) / (this.range.max - this.range.min) } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; t.StepAxis = t.Axis.extend({ constructor: function (e, i, n, s) { t.StepAxis.super.constructor.call(this, e, n, s.ticks, s); var r = Math.max(1, s.ticks.length - (s.stretch ? 1 : 0)); this.stepLength = this.axisLength / r }, projectValue: function (e, t) { return this.stepLength * t } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; var i = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: t.noop, type: void 0 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: t.noop, type: void 0, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, showLine: !0, showPoint: !0, showArea: !1, areaBase: 0, lineSmooth: !0, showGridBackground: !1, low: void 0, high: void 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, fullWidth: !1, reverseData: !1, classNames: { chart: "ct-chart-line", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", line: "ct-line", point: "ct-point", area: "ct-area", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; t.Line = t.Base.extend({ constructor: function (e, n, s, r) { t.Line.super.constructor.call(this, e, n, i, t.extend({}, i, s), r) }, createChart: function (e) { var n = t.normalizeData(this.data, e.reverseData, !0); this.svg = t.createSvg(this.container, e.width, e.height, e.classNames.chart); var s, r, a = this.svg.elem("g").addClass(e.classNames.gridGroup), o = this.svg.elem("g"), l = this.svg.elem("g").addClass(e.classNames.labelGroup), h = t.createChartRect(this.svg, e, i.padding); s = void 0 === e.axisX.type ? new t.StepAxis(t.Axis.units.x, n.normalized.series, h, t.extend({}, e.axisX, { ticks: n.normalized.labels, stretch: e.fullWidth })) : e.axisX.type.call(t, t.Axis.units.x, n.normalized.series, h, e.axisX), r = void 0 === e.axisY.type ? new t.AutoScaleAxis(t.Axis.units.y, n.normalized.series, h, t.extend({}, e.axisY, { high: t.isNumeric(e.high) ? e.high : e.axisY.high, low: t.isNumeric(e.low) ? e.low : e.axisY.low })) : e.axisY.type.call(t, t.Axis.units.y, n.normalized.series, h, e.axisY), s.createGridAndLabels(a, l, this.supportsForeignObject, e, this.eventEmitter), r.createGridAndLabels(a, l, this.supportsForeignObject, e, this.eventEmitter), e.showGridBackground && t.createGridBackground(a, h, e.classNames.gridBackground, this.eventEmitter), n.raw.series.forEach(function (i, a) { var l = o.elem("g"); l.attr({ "ct:series-name": i.name, "ct:meta": t.serialize(i.meta) }), l.addClass([e.classNames.series, i.className || e.classNames.series + "-" + t.alphaNumerate(a)].join(" ")); var u = [], c = []; n.normalized.series[a].forEach(function (e, o) { var l = { x: h.x1 + s.projectValue(e, o, n.normalized.series[a]), y: h.y1 - r.projectValue(e, o, n.normalized.series[a]) }; u.push(l.x, l.y), c.push({ value: e, valueIndex: o, meta: t.getMetaData(i, o) }) }.bind(this)); var d = { lineSmooth: t.getSeriesOption(i, e, "lineSmooth"), showPoint: t.getSeriesOption(i, e, "showPoint"), showLine: t.getSeriesOption(i, e, "showLine"), showArea: t.getSeriesOption(i, e, "showArea"), areaBase: t.getSeriesOption(i, e, "areaBase") }, p = ("function" == typeof d.lineSmooth ? d.lineSmooth : d.lineSmooth ? t.Interpolation.monotoneCubic() : t.Interpolation.none())(u, c); if (d.showPoint && p.pathElements.forEach(function (n) { var o = l.elem("line", { x1: n.x, y1: n.y, x2: n.x + .01, y2: n.y }, e.classNames.point).attr({ "ct:value": [n.data.value.x, n.data.value.y].filter(t.isNumeric).join(","), "ct:meta": t.serialize(n.data.meta) }); this.eventEmitter.emit("draw", { type: "point", value: n.data.value, index: n.data.valueIndex, meta: n.data.meta, series: i, seriesIndex: a, axisX: s, axisY: r, group: l, element: o, x: n.x, y: n.y }) }.bind(this)), d.showLine) { var f = l.elem("path", { d: p.stringify() }, e.classNames.line, !0); this.eventEmitter.emit("draw", { type: "line", values: n.normalized.series[a], path: p.clone(), chartRect: h, index: a, series: i, seriesIndex: a, seriesMeta: i.meta, axisX: s, axisY: r, group: l, element: f }) } if (d.showArea && r.range) { var m = Math.max(Math.min(d.areaBase, r.range.max), r.range.min), g = h.y1 - r.projectValue(m); p.splitByCommand("M").filter((function (e) { return e.pathElements.length > 1 })).map((function (e) { var t = e.pathElements[0], i = e.pathElements[e.pathElements.length - 1]; return e.clone(!0).position(0).remove(1).move(t.x, g).line(t.x, t.y).position(e.pathElements.length + 1).line(i.x, g) })).forEach(function (t) { var o = l.elem("path", { d: t.stringify() }, e.classNames.area, !0); this.eventEmitter.emit("draw", { type: "area", values: n.normalized.series[a], path: t.clone(), series: i, seriesIndex: a, axisX: s, axisY: r, chartRect: h, index: a, group: l, element: o }) }.bind(this)) } }.bind(this)), this.eventEmitter.emit("created", { bounds: r.bounds, chartRect: h, axisX: s, axisY: r, svg: this.svg, options: e }) } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; var i = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: t.noop, scaleMinSpace: 30, onlyInteger: !1 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: t.noop, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, high: void 0, low: void 0, referenceValue: 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, seriesBarDistance: 15, stackBars: !1, stackMode: "accumulate", horizontalBars: !1, distributeSeries: !1, reverseData: !1, showGridBackground: !1, classNames: { chart: "ct-chart-bar", horizontalBars: "ct-horizontal-bars", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", bar: "ct-bar", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; t.Bar = t.Base.extend({ constructor: function (e, n, s, r) { t.Bar.super.constructor.call(this, e, n, i, t.extend({}, i, s), r) }, createChart: function (e) { var n, s; e.distributeSeries ? (n = t.normalizeData(this.data, e.reverseData, e.horizontalBars ? "x" : "y")).normalized.series = n.normalized.series.map((function (e) { return [e] })) : n = t.normalizeData(this.data, e.reverseData, e.horizontalBars ? "x" : "y"), this.svg = t.createSvg(this.container, e.width, e.height, e.classNames.chart + (e.horizontalBars ? " " + e.classNames.horizontalBars : "")); var r = this.svg.elem("g").addClass(e.classNames.gridGroup), a = this.svg.elem("g"), o = this.svg.elem("g").addClass(e.classNames.labelGroup); if (e.stackBars && 0 !== n.normalized.series.length) { var l = t.serialMap(n.normalized.series, (function () { return Array.prototype.slice.call(arguments).map((function (e) { return e })).reduce((function (e, t) { return { x: e.x + (t && t.x) || 0, y: e.y + (t && t.y) || 0 } }), { x: 0, y: 0 }) })); s = t.getHighLow([l], e, e.horizontalBars ? "x" : "y") } else s = t.getHighLow(n.normalized.series, e, e.horizontalBars ? "x" : "y"); s.high = +e.high || (0 === e.high ? 0 : s.high), s.low = +e.low || (0 === e.low ? 0 : s.low); var h, u, c, d, p, f = t.createChartRect(this.svg, e, i.padding); u = e.distributeSeries && e.stackBars ? n.normalized.labels.slice(0, 1) : n.normalized.labels, e.horizontalBars ? (h = d = void 0 === e.axisX.type ? new t.AutoScaleAxis(t.Axis.units.x, n.normalized.series, f, t.extend({}, e.axisX, { highLow: s, referenceValue: 0 })) : e.axisX.type.call(t, t.Axis.units.x, n.normalized.series, f, t.extend({}, e.axisX, { highLow: s, referenceValue: 0 })), c = p = void 0 === e.axisY.type ? new t.StepAxis(t.Axis.units.y, n.normalized.series, f, { ticks: u }) : e.axisY.type.call(t, t.Axis.units.y, n.normalized.series, f, e.axisY)) : (c = d = void 0 === e.axisX.type ? new t.StepAxis(t.Axis.units.x, n.normalized.series, f, { ticks: u }) : e.axisX.type.call(t, t.Axis.units.x, n.normalized.series, f, e.axisX), h = p = void 0 === e.axisY.type ? new t.AutoScaleAxis(t.Axis.units.y, n.normalized.series, f, t.extend({}, e.axisY, { highLow: s, referenceValue: 0 })) : e.axisY.type.call(t, t.Axis.units.y, n.normalized.series, f, t.extend({}, e.axisY, { highLow: s, referenceValue: 0 }))); var m = e.horizontalBars ? f.x1 + h.projectValue(0) : f.y1 - h.projectValue(0), g = []; c.createGridAndLabels(r, o, this.supportsForeignObject, e, this.eventEmitter), h.createGridAndLabels(r, o, this.supportsForeignObject, e, this.eventEmitter), e.showGridBackground && t.createGridBackground(r, f, e.classNames.gridBackground, this.eventEmitter), n.raw.series.forEach(function (i, s) { var r, o, l = s - (n.raw.series.length - 1) / 2; r = e.distributeSeries && !e.stackBars ? c.axisLength / n.normalized.series.length / 2 : e.distributeSeries && e.stackBars ? c.axisLength / 2 : c.axisLength / n.normalized.series[s].length / 2, (o = a.elem("g")).attr({ "ct:series-name": i.name, "ct:meta": t.serialize(i.meta) }), o.addClass([e.classNames.series, i.className || e.classNames.series + "-" + t.alphaNumerate(s)].join(" ")), n.normalized.series[s].forEach(function (a, u) { var v, x, y, b; if (b = e.distributeSeries && !e.stackBars ? s : e.distributeSeries && e.stackBars ? 0 : u, v = e.horizontalBars ? { x: f.x1 + h.projectValue(a && a.x ? a.x : 0, u, n.normalized.series[s]), y: f.y1 - c.projectValue(a && a.y ? a.y : 0, b, n.normalized.series[s]) } : { x: f.x1 + c.projectValue(a && a.x ? a.x : 0, b, n.normalized.series[s]), y: f.y1 - h.projectValue(a && a.y ? a.y : 0, u, n.normalized.series[s]) }, c instanceof t.StepAxis && (c.options.stretch || (v[c.units.pos] += r * (e.horizontalBars ? -1 : 1)), v[c.units.pos] += e.stackBars || e.distributeSeries ? 0 : l * e.seriesBarDistance * (e.horizontalBars ? -1 : 1)), y = g[u] || m, g[u] = y - (m - v[c.counterUnits.pos]), void 0 !== a) { var w = {}; w[c.units.pos + "1"] = v[c.units.pos], w[c.units.pos + "2"] = v[c.units.pos], !e.stackBars || "accumulate" !== e.stackMode && e.stackMode ? (w[c.counterUnits.pos + "1"] = m, w[c.counterUnits.pos + "2"] = v[c.counterUnits.pos]) : (w[c.counterUnits.pos + "1"] = y, w[c.counterUnits.pos + "2"] = g[u]), w.x1 = Math.min(Math.max(w.x1, f.x1), f.x2), w.x2 = Math.min(Math.max(w.x2, f.x1), f.x2), w.y1 = Math.min(Math.max(w.y1, f.y2), f.y1), w.y2 = Math.min(Math.max(w.y2, f.y2), f.y1); var E = t.getMetaData(i, u); x = o.elem("line", w, e.classNames.bar).attr({ "ct:value": [a.x, a.y].filter(t.isNumeric).join(","), "ct:meta": t.serialize(E) }), this.eventEmitter.emit("draw", t.extend({ type: "bar", value: a, index: u, meta: E, series: i, seriesIndex: s, axisX: d, axisY: p, chartRect: f, group: o, element: x }, w)) } }.bind(this)) }.bind(this)), this.eventEmitter.emit("created", { bounds: h.bounds, chartRect: f, axisX: d, axisY: p, svg: this.svg, options: e }) } }) }(this || global, e), function (e, t) { "use strict"; e.window, e.document; var i = { width: void 0, height: void 0, chartPadding: 5, classNames: { chartPie: "ct-chart-pie", chartDonut: "ct-chart-donut", series: "ct-series", slicePie: "ct-slice-pie", sliceDonut: "ct-slice-donut", sliceDonutSolid: "ct-slice-donut-solid", label: "ct-label" }, startAngle: 0, total: void 0, donut: !1, donutSolid: !1, donutWidth: 60, showLabel: !0, labelOffset: 0, labelPosition: "inside", labelInterpolationFnc: t.noop, labelDirection: "neutral", reverseData: !1, ignoreEmptyValues: !1 }; function n(e, t, i) { var n = t.x > e.x; return n && "explode" === i || !n && "implode" === i ? "start" : n && "implode" === i || !n && "explode" === i ? "end" : "middle" } t.Pie = t.Base.extend({ constructor: function (e, n, s, r) { t.Pie.super.constructor.call(this, e, n, i, t.extend({}, i, s), r) }, createChart: function (e) { var s, r, a, o, l, h = t.normalizeData(this.data), u = [], c = e.startAngle; this.svg = t.createSvg(this.container, e.width, e.height, e.donut ? e.classNames.chartDonut : e.classNames.chartPie), r = t.createChartRect(this.svg, e, i.padding), a = Math.min(r.width() / 2, r.height() / 2), l = e.total || h.normalized.series.reduce((function (e, t) { return e + t }), 0); var d = t.quantity(e.donutWidth); "%" === d.unit && (d.value *= a / 100), a -= e.donut && !e.donutSolid ? d.value / 2 : 0, o = "outside" === e.labelPosition || e.donut && !e.donutSolid ? a : "center" === e.labelPosition ? 0 : e.donutSolid ? a - d.value / 2 : a / 2, o += e.labelOffset; var p = { x: r.x1 + r.width() / 2, y: r.y2 + r.height() / 2 }, f = 1 === h.raw.series.filter((function (e) { return e.hasOwnProperty("value") ? 0 !== e.value : 0 !== e })).length; h.raw.series.forEach(function (e, t) { u[t] = this.svg.elem("g", null, null) }.bind(this)), e.showLabel && (s = this.svg.elem("g", null, null)), h.raw.series.forEach(function (i, r) { if (0 !== h.normalized.series[r] || !e.ignoreEmptyValues) { u[r].attr({ "ct:series-name": i.name }), u[r].addClass([e.classNames.series, i.className || e.classNames.series + "-" + t.alphaNumerate(r)].join(" ")); var m = l > 0 ? c + h.normalized.series[r] / l * 360 : 0, g = Math.max(0, c - (0 === r || f ? 0 : .2)); m - g >= 359.99 && (m = g + 359.99); var v, x, y, b = t.polarToCartesian(p.x, p.y, a, g), w = t.polarToCartesian(p.x, p.y, a, m), E = new t.Svg.Path(!e.donut || e.donutSolid).move(w.x, w.y).arc(a, a, 0, m - c > 180, 0, b.x, b.y); e.donut ? e.donutSolid && (y = a - d.value, v = t.polarToCartesian(p.x, p.y, y, c - (0 === r || f ? 0 : .2)), x = t.polarToCartesian(p.x, p.y, y, m), E.line(v.x, v.y), E.arc(y, y, 0, m - c > 180, 1, x.x, x.y)) : E.line(p.x, p.y); var S = e.classNames.slicePie; e.donut && (S = e.classNames.sliceDonut, e.donutSolid && (S = e.classNames.sliceDonutSolid)); var A = u[r].elem("path", { d: E.stringify() }, S); if (A.attr({ "ct:value": h.normalized.series[r], "ct:meta": t.serialize(i.meta) }), e.donut && !e.donutSolid && (A._node.style.strokeWidth = d.value + "px"), this.eventEmitter.emit("draw", { type: "slice", value: h.normalized.series[r], totalDataSum: l, index: r, meta: i.meta, series: i, group: u[r], element: A, path: E.clone(), center: p, radius: a, startAngle: c, endAngle: m }), e.showLabel) { var z, M; z = 1 === h.raw.series.length ? { x: p.x, y: p.y } : t.polarToCartesian(p.x, p.y, o, c + (m - c) / 2), M = h.normalized.labels && !t.isFalseyButZero(h.normalized.labels[r]) ? h.normalized.labels[r] : h.normalized.series[r]; var O = e.labelInterpolationFnc(M, r); if (O || 0 === O) { var C = s.elem("text", { dx: z.x, dy: z.y, "text-anchor": n(p, z, e.labelDirection) }, e.classNames.label).text("" + O); this.eventEmitter.emit("draw", { type: "label", index: r, group: s, element: C, text: "" + O, x: z.x, y: z.y }) } } c = m } }.bind(this)), this.eventEmitter.emit("created", { chartRect: r, svg: this.svg, options: e }) }, determineAnchorPosition: n }) }(this || global, e), e })); + +var i, l, selectedLine = null; + +/* Navigate to hash without browser history entry */ +var navigateToHash = function () { + if (window.history !== undefined && window.history.replaceState !== undefined) { + window.history.replaceState(undefined, undefined, this.getAttribute("href")); + } +}; + +var hashLinks = document.getElementsByClassName('navigatetohash'); +for (i = 0, l = hashLinks.length; i < l; i++) { + hashLinks[i].addEventListener('click', navigateToHash); +} + +/* Switch test method */ +var switchTestMethod = function () { + var method = this.getAttribute("value"); + console.log("Selected test method: " + method); + + var lines, i, l, coverageData, lineAnalysis, cells; + + lines = document.querySelectorAll('.lineAnalysis tr'); + + for (i = 1, l = lines.length; i < l; i++) { + coverageData = JSON.parse(lines[i].getAttribute('data-coverage').replace(/'/g, '"')); + lineAnalysis = coverageData[method]; + cells = lines[i].querySelectorAll('td'); + if (lineAnalysis === undefined) { + lineAnalysis = coverageData.AllTestMethods; + if (lineAnalysis.LVS !== 'gray') { + cells[0].setAttribute('class', 'red'); + cells[1].innerText = cells[1].textContent = '0'; + cells[4].setAttribute('class', 'lightred'); + } + } else { + cells[0].setAttribute('class', lineAnalysis.LVS); + cells[1].innerText = cells[1].textContent = lineAnalysis.VC; + cells[4].setAttribute('class', 'light' + lineAnalysis.LVS); + } + } +}; + +var testMethods = document.getElementsByClassName('switchtestmethod'); +for (i = 0, l = testMethods.length; i < l; i++) { + testMethods[i].addEventListener('change', switchTestMethod); +} + +/* Highlight test method by line */ +var toggleLine = function () { + if (selectedLine === this) { + selectedLine = null; + } else { + selectedLine = null; + unhighlightTestMethods(); + highlightTestMethods.call(this); + selectedLine = this; + } + +}; +var highlightTestMethods = function () { + if (selectedLine !== null) { + return; + } + + var lineAnalysis; + var coverageData = JSON.parse(this.getAttribute('data-coverage').replace(/'/g, '"')); + var testMethods = document.getElementsByClassName('testmethod'); + + for (i = 0, l = testMethods.length; i < l; i++) { + lineAnalysis = coverageData[testMethods[i].id]; + if (lineAnalysis === undefined) { + testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); + } else { + testMethods[i].className += ' light' + lineAnalysis.LVS; + } + } +}; +var unhighlightTestMethods = function () { + if (selectedLine !== null) { + return; + } + + var testMethods = document.getElementsByClassName('testmethod'); + for (i = 0, l = testMethods.length; i < l; i++) { + testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); + } +}; +var coverableLines = document.getElementsByClassName('coverableline'); +for (i = 0, l = coverableLines.length; i < l; i++) { + coverableLines[i].addEventListener('click', toggleLine); + coverableLines[i].addEventListener('mouseenter', highlightTestMethods); + coverableLines[i].addEventListener('mouseleave', unhighlightTestMethods); +} + +/* History charts */ +var renderChart = function (chart) { + // Remove current children (e.g. PNG placeholder) + while (chart.firstChild) { + chart.firstChild.remove(); + } + + var chartData = window[chart.getAttribute('data-data')]; + var options = { + axisY: { + type: undefined, + onlyInteger: true + }, + lineSmooth: false, + low: 0, + high: 100, + scaleMinSpace: 20, + onlyInteger: true, + fullWidth: true + }; + var lineChart = new Chartist.Line(chart, { + labels: [], + series: chartData.series + }, options); + + /* Zoom */ + var zoomButtonDiv = document.createElement("div"); + zoomButtonDiv.className = "toggleZoom"; + var zoomButtonLink = document.createElement("a"); + zoomButtonLink.setAttribute("href", ""); + var zoomButtonText = document.createElement("i"); + zoomButtonText.className = "icon-search-plus"; + + zoomButtonLink.appendChild(zoomButtonText); + zoomButtonDiv.appendChild(zoomButtonLink); + + chart.appendChild(zoomButtonDiv); + + zoomButtonDiv.addEventListener('click', function (event) { + event.preventDefault(); + + if (options.axisY.type === undefined) { + options.axisY.type = Chartist.AutoScaleAxis; + zoomButtonText.className = "icon-search-minus"; + } else { + options.axisY.type = undefined; + zoomButtonText.className = "icon-search-plus"; + } + + lineChart.update(null, options); + }); + + var tooltip = document.createElement("div"); + tooltip.className = "tooltip"; + + chart.appendChild(tooltip); + + /* Tooltips */ + var showToolTip = function () { + var index = this.getAttribute('ct:meta'); + + tooltip.innerHTML = chartData.tooltips[index]; + tooltip.style.display = 'block'; + }; + + var moveToolTip = function (event) { + var box = chart.getBoundingClientRect(); + var left = event.pageX - box.left - window.pageXOffset; + var top = event.pageY - box.top - window.pageYOffset; + + left = left + 20; + top = top - tooltip.offsetHeight / 2; + + if (left + tooltip.offsetWidth > box.width) { + left -= tooltip.offsetWidth + 40; + } + + if (top < 0) { + top = 0; + } + + if (top + tooltip.offsetHeight > box.height) { + top = box.height - tooltip.offsetHeight; + } + + tooltip.style.left = left + 'px'; + tooltip.style.top = top + 'px'; + }; + + var hideToolTip = function () { + tooltip.style.display = 'none'; + }; + chart.addEventListener('mousemove', moveToolTip); + + lineChart.on('created', function () { + var chartPoints = chart.getElementsByClassName('ct-point'); + for (i = 0, l = chartPoints.length; i < l; i++) { + chartPoints[i].addEventListener('mousemove', showToolTip); + chartPoints[i].addEventListener('mouseout', hideToolTip); + } + }); +}; + +var charts = document.getElementsByClassName('historychart'); +for (i = 0, l = charts.length; i < l; i++) { + renderChart(charts[i]); +} + +var assemblies = [ + { + "name": "ExchangeRateUpdater.Abstraction", + "classes": [ + { "name": "ExchangeRateUpdater.Abstraction.Model.Currency", "rp": "ExchangeRateUpdater.Abstraction_Currency.html", "cl": 18, "ucl": 0, "cal": 18, "tl": 48, "cb": 7, "tb": 8, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + { "name": "ExchangeRateUpdater.Abstraction.Model.ExchangeRate", "rp": "ExchangeRateUpdater.Abstraction_ExchangeRate.html", "cl": 16, "ucl": 0, "cal": 16, "tl": 55, "cb": 2, "tb": 2, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + ]}, + { + "name": "ExchangeRateUpdater.Api", + "classes": [ + { "name": "ExchangeRateUpdater.Api.Controllers.ExchangeRateController", "rp": "ExchangeRateUpdater.Api_ExchangeRateController.html", "cl": 13, "ucl": 0, "cal": 13, "tl": 31, "cb": 2, "tb": 2, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + { "name": "ExchangeRateUpdater.Api.Middleware.ExceptionHandlingMiddleware", "rp": "ExchangeRateUpdater.Api_ExceptionHandlingMiddleware.html", "cl": 12, "ucl": 18, "cal": 30, "tl": 70, "cb": 0, "tb": 4, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + { "name": "Microsoft.AspNetCore.OpenApi.Generated", "rp": "ExchangeRateUpdater.Api_Generated.html", "cl": 0, "ucl": 374, "cal": 374, "tl": 592, "cb": 0, "tb": 206, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + { "name": "Program", "rp": "ExchangeRateUpdater.Api_Program.html", "cl": 26, "ucl": 4, "cal": 30, "tl": 55, "cb": 2, "tb": 2, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + { "name": "System.Runtime.CompilerServices", "rp": "ExchangeRateUpdater.Api_CompilerServices.html", "cl": 0, "ucl": 3, "cal": 3, "tl": 23, "cb": 0, "tb": 0, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + ]}, + { + "name": "ExchangeRateUpdater.CbnApiClient", + "classes": [ + { "name": "ExchangeRateUpdater.CbnApiClient.Implementation.ExchangeRateCnbApiClient", "rp": "ExchangeRateUpdater.CbnApiClient_ExchangeRateCnbApiClient.html", "cl": 31, "ucl": 1, "cal": 32, "tl": 68, "cb": 8, "tb": 10, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + { "name": "ExchangeRateUpdater.CbnApiClient.Mapper.CnbExchangeRateMapperExtensions", "rp": "ExchangeRateUpdater.CbnApiClient_CnbExchangeRateMapperExtensions.html", "cl": 15, "ucl": 0, "cal": 15, "tl": 53, "cb": 2, "tb": 2, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + ]}, + { + "name": "ExchangeRateUpdater.DependencyInjection", + "classes": [ + { "name": "ExchangeRateUpdater.DependencyInjection.ExchangeRateCnbServicesExtension", "rp": "ExchangeRateUpdater.DependencyInjection_ExchangeRateCnbServicesExtension.html", "cl": 11, "ucl": 1, "cal": 12, "tl": 37, "cb": 0, "tb": 0, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + ]}, + { + "name": "ExchangeRateUpdater.Service", + "classes": [ + { "name": "ExchangeRateUpdater.Service.ExchangeRateProvider", "rp": "ExchangeRateUpdater.Service_ExchangeRateProvider.html", "cl": 25, "ucl": 3, "cal": 28, "tl": 59, "cb": 5, "tb": 8, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + { "name": "ExchangeRateUpdater.Service.ExchangeRateService", "rp": "ExchangeRateUpdater.Service_ExchangeRateService.html", "cl": 21, "ucl": 0, "cal": 21, "tl": 49, "cb": 4, "tb": 4, "cm": 0, "fcm": 0, "tm": 0, "lch": [], "bch": [], "mch": [], "mfch": [], "hc": [], "metrics": { } }, + ]}, +]; + +var metrics = [{ "name": "Crap Score", "abbreviation": "crp", "explanationUrl": "https://googletesting.blogspot.de/2011/02/this-code-is-crap.html" }, { "name": "Cyclomatic complexity", "abbreviation": "cc", "explanationUrl": "https://en.wikipedia.org/wiki/Cyclomatic_complexity" }, { "name": "Line coverage", "abbreviation": "cov", "explanationUrl": "https://en.wikipedia.org/wiki/Code_coverage" }, { "name": "Branch coverage", "abbreviation": "bcov", "explanationUrl": "https://en.wikipedia.org/wiki/Code_coverage" }]; + +var historicCoverageExecutionTimes = []; + +var riskHotspotMetrics = [ + { "name": "Crap Score", "explanationUrl": "https://googletesting.blogspot.de/2011/02/this-code-is-crap.html" }, + { "name": "Cyclomatic complexity", "explanationUrl": "https://en.wikipedia.org/wiki/Cyclomatic_complexity" }, +]; + +var riskHotspots = [ + { + "assembly": "ExchangeRateUpdater.Api", "class": "Microsoft.AspNetCore.OpenApi.Generated", "reportPath": "ExchangeRateUpdater.Api_Generated.html", "methodName": "TransformAsync(Microsoft.OpenApi.OpenApiOperation,Microsoft.AspNetCore.OpenApi.OpenApiOperationTransformerContext,System.Threading.CancellationToken)", "methodShortName": "TransformAsync(...)", "fileIndex": 0, "line": 345, + "metrics": [ + { "value": 8930, "exceeded": true }, + { "value": 94, "exceeded": true }, + ]}, + { + "assembly": "ExchangeRateUpdater.Api", "class": "Microsoft.AspNetCore.OpenApi.Generated", "reportPath": "ExchangeRateUpdater.Api_Generated.html", "methodName": "TransformAsync(Microsoft.OpenApi.OpenApiSchema,Microsoft.AspNetCore.OpenApi.OpenApiSchemaTransformerContext,System.Threading.CancellationToken)", "methodShortName": "TransformAsync(...)", "fileIndex": 0, "line": 511, + "metrics": [ + { "value": 1190, "exceeded": true }, + { "value": 34, "exceeded": true }, + ]}, + { + "assembly": "ExchangeRateUpdater.Api", "class": "Microsoft.AspNetCore.OpenApi.Generated", "reportPath": "ExchangeRateUpdater.Api_Generated.html", "methodName": "GetTypeDocId(System.Type,System.Boolean,System.Boolean)", "methodShortName": "GetTypeDocId(...)", "fileIndex": 0, "line": 238, + "metrics": [ + { "value": 812, "exceeded": true }, + { "value": 28, "exceeded": true }, + ]}, + { + "assembly": "ExchangeRateUpdater.Api", "class": "Microsoft.AspNetCore.OpenApi.Generated", "reportPath": "ExchangeRateUpdater.Api_Generated.html", "methodName": "CreateDocumentationId(System.Reflection.MethodInfo)", "methodShortName": "CreateDocumentationId(...)", "fileIndex": 0, "line": 168, + "metrics": [ + { "value": 342, "exceeded": true }, + { "value": 18, "exceeded": true }, + ]}, + { + "assembly": "ExchangeRateUpdater.Api", "class": "Microsoft.AspNetCore.OpenApi.Generated", "reportPath": "ExchangeRateUpdater.Api_Generated.html", "methodName": "CreateDocumentationId(System.Reflection.PropertyInfo)", "methodShortName": "CreateDocumentationId(...)", "fileIndex": 0, "line": 100, + "metrics": [ + { "value": 110, "exceeded": true }, + { "value": 10, "exceeded": false }, + ]}, + { + "assembly": "ExchangeRateUpdater.Api", "class": "Microsoft.AspNetCore.OpenApi.Generated", "reportPath": "ExchangeRateUpdater.Api_Generated.html", "methodName": "NormalizeDocId(System.String)", "methodShortName": "NormalizeDocId(...)", "fileIndex": 0, "line": 320, + "metrics": [ + { "value": 42, "exceeded": true }, + { "value": 6, "exceeded": false }, + ]}, + { + "assembly": "ExchangeRateUpdater.Api", "class": "Microsoft.AspNetCore.OpenApi.Generated", "reportPath": "ExchangeRateUpdater.Api_Generated.html", "methodName": "UnwrapOpenApiParameter(Microsoft.OpenApi.IOpenApiParameter)", "methodShortName": "UnwrapOpenApiParameter(...)", "fileIndex": 0, "line": 484, + "metrics": [ + { "value": 42, "exceeded": true }, + { "value": 6, "exceeded": false }, + ]}, +]; + +var branchCoverageAvailable = true; +var methodCoverageAvailable = false; +var applyMaximumGroupingLevel = false; +var maximumDecimalPlacesForCoverageQuotas = 1; + + +var translations = { +'top': 'Top:', +'all': 'All', +'assembly': 'Assembly', +'class': 'Class', +'method': 'Method', +'lineCoverage': 'Line coverage', +'noGrouping': 'No grouping', +'byAssembly': 'By assembly', +'byNamespace': 'By namespace, Level:', +'all': 'All', +'collapseAll': 'Collapse all', +'expandAll': 'Expand all', +'grouping': 'Grouping:', +'filter': 'Filter:', +'name': 'Name', +'covered': 'Covered', +'uncovered': 'Uncovered', +'coverable': 'Coverable', +'total': 'Total', +'coverage': 'Line coverage', +'branchCoverage': 'Branch coverage', +'methodCoverage': 'Method coverage', +'fullMethodCoverage': 'Full method coverage', +'percentage': 'Percentage', +'history': 'Coverage history', +'compareHistory': 'Compare with:', +'date': 'Date', +'allChanges': 'All changes', +'selectCoverageTypes': 'Select coverage types', +'selectCoverageTypesAndMetrics': 'Select coverage types & metrics', +'coverageTypes': 'Coverage types', +'metrics': 'Metrics', +'methodCoverageProVersion': 'Feature is only available for sponsors', +'lineCoverageIncreaseOnly': 'Line coverage: Increase only', +'lineCoverageDecreaseOnly': 'Line coverage: Decrease only', +'branchCoverageIncreaseOnly': 'Branch coverage: Increase only', +'branchCoverageDecreaseOnly': 'Branch coverage: Decrease only', +'methodCoverageIncreaseOnly': 'Method coverage: Increase only', +'methodCoverageDecreaseOnly': 'Method coverage: Decrease only', +'fullMethodCoverageIncreaseOnly': 'Full method coverage: Increase only', +'fullMethodCoverageDecreaseOnly': 'Full method coverage: Decrease only' +}; + + +(()=>{"use strict";var e,_={},p={};function n(e){var a=p[e];if(void 0!==a)return a.exports;var r=p[e]={exports:{}};return _[e](r,r.exports,n),r.exports}n.m=_,e=[],n.O=(a,r,u,l)=>{if(!r){var o=1/0;for(f=0;f=l)&&Object.keys(n.O).every(h=>n.O[h](r[t]))?r.splice(t--,1):(v=!1,l0&&e[f-1][2]>l;f--)e[f]=e[f-1];e[f]=[r,u,l]},n.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return n.d(a,{a}),a},n.d=(e,a)=>{for(var r in a)n.o(a,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:a[r]})},n.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),(()=>{var e={121:0};n.O.j=u=>0===e[u];var a=(u,l)=>{var t,c,[f,o,v]=l,s=0;if(f.some(d=>0!==e[d])){for(t in o)n.o(o,t)&&(n.m[t]=o[t]);if(v)var b=v(n)}for(u&&u(l);s{ve(935)},935:()=>{const te=globalThis;function Q(t){return(te.__Zone_symbol_prefix||"__zone_symbol__")+t}const Ee=Object.getOwnPropertyDescriptor,Le=Object.defineProperty,Ie=Object.getPrototypeOf,_t=Object.create,Et=Array.prototype.slice,Me="addEventListener",Ze="removeEventListener",Ae=Q(Me),je=Q(Ze),ae="true",le="false",Pe=Q("");function He(t,r){return Zone.current.wrap(t,r)}function xe(t,r,i,n,s){return Zone.current.scheduleMacroTask(t,r,i,n,s)}const H=Q,Ce=typeof window<"u",Te=Ce?window:void 0,$=Ce&&Te||globalThis;function Ve(t,r){for(let i=t.length-1;i>=0;i--)"function"==typeof t[i]&&(t[i]=He(t[i],r+"_"+i));return t}function We(t){return!t||!1!==t.writable&&!("function"==typeof t.get&&typeof t.set>"u")}const qe=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope,De=!("nw"in $)&&typeof $.process<"u"&&"[object process]"===$.process.toString(),Ge=!De&&!qe&&!(!Ce||!Te.HTMLElement),Xe=typeof $.process<"u"&&"[object process]"===$.process.toString()&&!qe&&!(!Ce||!Te.HTMLElement),Se={},pt=H("enable_beforeunload"),Ye=function(t){if(!(t=t||$.event))return;let r=Se[t.type];r||(r=Se[t.type]=H("ON_PROPERTY"+t.type));const i=this||t.target||$,n=i[r];let s;return Ge&&i===Te&&"error"===t.type?(s=n&&n.call(this,t.message,t.filename,t.lineno,t.colno,t.error),!0===s&&t.preventDefault()):(s=n&&n.apply(this,arguments),"beforeunload"===t.type&&$[pt]&&"string"==typeof s?t.returnValue=s:null!=s&&!s&&t.preventDefault()),s};function $e(t,r,i){let n=Ee(t,r);if(!n&&i&&Ee(i,r)&&(n={enumerable:!0,configurable:!0}),!n||!n.configurable)return;const s=H("on"+r+"patched");if(t.hasOwnProperty(s)&&t[s])return;delete n.writable,delete n.value;const f=n.get,T=n.set,g=r.slice(2);let m=Se[g];m||(m=Se[g]=H("ON_PROPERTY"+g)),n.set=function(C){let E=this;!E&&t===$&&(E=$),E&&("function"==typeof E[m]&&E.removeEventListener(g,Ye),T&&T.call(E,null),E[m]=C,"function"==typeof C&&E.addEventListener(g,Ye,!1))},n.get=function(){let C=this;if(!C&&t===$&&(C=$),!C)return null;const E=C[m];if(E)return E;if(f){let P=f.call(this);if(P)return n.set.call(this,P),"function"==typeof C.removeAttribute&&C.removeAttribute(r),P}return null},Le(t,r,n),t[s]=!0}function Ke(t,r,i){if(r)for(let n=0;nfunction(T,g){const m=i(T,g);return m.cbIdx>=0&&"function"==typeof g[m.cbIdx]?xe(m.name,g[m.cbIdx],m,s):f.apply(T,g)})}function fe(t,r){t[H("OriginalDelegate")]=r}let Je=!1,Be=!1;function kt(){if(Je)return Be;Je=!0;try{const t=Te.navigator.userAgent;(-1!==t.indexOf("MSIE ")||-1!==t.indexOf("Trident/")||-1!==t.indexOf("Edge/"))&&(Be=!0)}catch{}return Be}function Qe(t){return"function"==typeof t}function et(t){return"number"==typeof t}let ge=!1;if(typeof window<"u")try{const t=Object.defineProperty({},"passive",{get:function(){ge=!0}});window.addEventListener("test",t,t),window.removeEventListener("test",t,t)}catch{ge=!1}const vt={useG:!0},ne={},tt={},nt=new RegExp("^"+Pe+"(\\w+)(true|false)$"),rt=H("propagationStopped");function ot(t,r){const i=(r?r(t):t)+le,n=(r?r(t):t)+ae,s=Pe+i,f=Pe+n;ne[t]={},ne[t][le]=s,ne[t][ae]=f}function bt(t,r,i,n){const s=n&&n.add||Me,f=n&&n.rm||Ze,T=n&&n.listeners||"eventListeners",g=n&&n.rmAll||"removeAllListeners",m=H(s),C="."+s+":",E="prependListener",P="."+E+":",A=function(k,h,x){if(k.isRemoved)return;const G=k.callback;let Y;"object"==typeof G&&G.handleEvent&&(k.callback=p=>G.handleEvent(p),k.originalDelegate=G);try{k.invoke(k,h,[x])}catch(p){Y=p}const B=k.options;return B&&"object"==typeof B&&B.once&&h[f].call(h,x.type,k.originalDelegate?k.originalDelegate:k.callback,B),Y};function V(k,h,x){if(!(h=h||t.event))return;const G=k||h.target||t,Y=G[ne[h.type][x?ae:le]];if(Y){const B=[];if(1===Y.length){const p=A(Y[0],G,h);p&&B.push(p)}else{const p=Y.slice();for(let W=0;W{throw W})}}}const z=function(k){return V(this,k,!1)},K=function(k){return V(this,k,!0)};function J(k,h){if(!k)return!1;let x=!0;h&&void 0!==h.useG&&(x=h.useG);const G=h&&h.vh;let Y=!0;h&&void 0!==h.chkDup&&(Y=h.chkDup);let B=!1;h&&void 0!==h.rt&&(B=h.rt);let p=k;for(;p&&!p.hasOwnProperty(s);)p=Ie(p);if(!p&&k[s]&&(p=k),!p||p[m])return!1;const W=h&&h.eventNameToString,L={},w=p[m]=p[s],b=p[H(f)]=p[f],S=p[H(T)]=p[T],ee=p[H(g)]=p[g];let q;h&&h.prepend&&(q=p[H(h.prepend)]=p[h.prepend]);const N=x?function(o){if(!L.isExisting)return w.call(L.target,L.eventName,L.capture?K:z,L.options)}:function(o){return w.call(L.target,L.eventName,o.invoke,L.options)},D=x?function(o){if(!o.isRemoved){const u=ne[o.eventName];let v;u&&(v=u[o.capture?ae:le]);const R=v&&o.target[v];if(R)for(let y=0;yse.zone.cancelTask(se);o.call(me,"abort",ce,{once:!0}),se.removeAbortListener=()=>me.removeEventListener("abort",ce)}return L.target=null,Re&&(Re.taskData=null),lt&&(L.options.once=!0),!ge&&"boolean"==typeof se.options||(se.options=ie),se.target=M,se.capture=Ue,se.eventName=Z,U&&(se.originalDelegate=F),I?ke.unshift(se):ke.push(se),y?M:void 0}};return p[s]=a(w,C,N,D,B),q&&(p[E]=a(q,P,function(o){return q.call(L.target,L.eventName,o.invoke,L.options)},D,B,!0)),p[f]=function(){const o=this||t;let u=arguments[0];h&&h.transferEventName&&(u=h.transferEventName(u));const v=arguments[2],R=!!v&&("boolean"==typeof v||v.capture),y=arguments[1];if(!y)return b.apply(this,arguments);if(G&&!G(b,y,o,arguments))return;const I=ne[u];let M;I&&(M=I[R?ae:le]);const Z=M&&o[M];if(Z)for(let F=0;Ffunction(s,f){s[rt]=!0,n&&n.apply(s,f)})}const Oe=H("zoneTask");function pe(t,r,i,n){let s=null,f=null;i+=n;const T={};function g(C){const E=C.data;E.args[0]=function(){return C.invoke.apply(this,arguments)};const P=s.apply(t,E.args);return et(P)?E.handleId=P:(E.handle=P,E.isRefreshable=Qe(P.refresh)),C}function m(C){const{handle:E,handleId:P}=C.data;return f.call(t,E??P)}s=ue(t,r+=n,C=>function(E,P){if(Qe(P[0])){const A={isRefreshable:!1,isPeriodic:"Interval"===n,delay:"Timeout"===n||"Interval"===n?P[1]||0:void 0,args:P},V=P[0];P[0]=function(){try{return V.apply(this,arguments)}finally{const{handle:x,handleId:G,isPeriodic:Y,isRefreshable:B}=A;!Y&&!B&&(G?delete T[G]:x&&(x[Oe]=null))}};const z=xe(r,P[0],A,g,m);if(!z)return z;const{handleId:K,handle:J,isRefreshable:X,isPeriodic:k}=z.data;if(K)T[K]=z;else if(J&&(J[Oe]=z,X&&!k)){const h=J.refresh;J.refresh=function(){const{zone:x,state:G}=z;return"notScheduled"===G?(z._state="scheduled",x._updateTaskCount(z,1)):"running"===G&&(z._state="scheduling"),h.call(this)}}return J??K??z}return C.apply(t,P)}),f=ue(t,i,C=>function(E,P){const A=P[0];let V;et(A)?(V=T[A],delete T[A]):(V=A?.[Oe],V?A[Oe]=null:V=A),V?.type?V.cancelFn&&V.zone.cancelTask(V):C.apply(t,P)})}function it(t,r,i){if(!i||0===i.length)return r;const n=i.filter(f=>f.target===t);if(!n||0===n.length)return r;const s=n[0].ignoreProperties;return r.filter(f=>-1===s.indexOf(f))}function ct(t,r,i,n){t&&Ke(t,it(t,r,i),n)}function Fe(t){return Object.getOwnPropertyNames(t).filter(r=>r.startsWith("on")&&r.length>2).map(r=>r.substring(2))}function It(t,r,i,n,s){const f=Zone.__symbol__(n);if(r[f])return;const T=r[f]=r[n];r[n]=function(g,m,C){return m&&m.prototype&&s.forEach(function(E){const P=`${i}.${n}::`+E,A=m.prototype;try{if(A.hasOwnProperty(E)){const V=t.ObjectGetOwnPropertyDescriptor(A,E);V&&V.value?(V.value=t.wrapWithCurrentZone(V.value,P),t._redefineProperty(m.prototype,E,V)):A[E]&&(A[E]=t.wrapWithCurrentZone(A[E],P))}else A[E]&&(A[E]=t.wrapWithCurrentZone(A[E],P))}catch{}}),T.call(r,g,m,C)},t.attachOriginToPatched(r[n],T)}const at=function be(){const t=globalThis,r=!0===t[Q("forceDuplicateZoneCheck")];if(t.Zone&&(r||"function"!=typeof t.Zone.__symbol__))throw new Error("Zone already loaded.");return t.Zone??=function ve(){const t=te.performance;function r(j){t&&t.mark&&t.mark(j)}function i(j,_){t&&t.measure&&t.measure(j,_)}r("Zone");let n=(()=>{var j;class _{static assertZonePatched(){if(te.Promise!==L.ZoneAwarePromise)throw new Error("Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten.\nMost likely cause is that a Promise polyfill has been loaded after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. If you must load one, do so before loading zone.js.)")}static get root(){let e=_.current;for(;e.parent;)e=e.parent;return e}static get current(){return b.zone}static get currentTask(){return S}static __load_patch(e,d,O=!1){if(L.hasOwnProperty(e)){const N=!0===te[Q("forceDuplicateZoneCheck")];if(!O&&N)throw Error("Already loaded patch: "+e)}else if(!te["__Zone_disable_"+e]){const N="Zone:"+e;r(N),L[e]=d(te,_,w),i(N,N)}}get parent(){return this._parent}get name(){return this._name}constructor(e,d){this._parent=e,this._name=d?d.name||"unnamed":"",this._properties=d&&d.properties||{},this._zoneDelegate=new f(this,this._parent&&this._parent._zoneDelegate,d)}get(e){const d=this.getZoneWith(e);if(d)return d._properties[e]}getZoneWith(e){let d=this;for(;d;){if(d._properties.hasOwnProperty(e))return d;d=d._parent}return null}fork(e){if(!e)throw new Error("ZoneSpec required!");return this._zoneDelegate.fork(this,e)}wrap(e,d){if("function"!=typeof e)throw new Error("Expecting function got: "+e);const O=this._zoneDelegate.intercept(this,e,d),N=this;return function(){return N.runGuarded(O,this,arguments,d)}}run(e,d,O,N){b={parent:b,zone:this};try{return this._zoneDelegate.invoke(this,e,d,O,N)}finally{b=b.parent}}runGuarded(e,d=null,O,N){b={parent:b,zone:this};try{try{return this._zoneDelegate.invoke(this,e,d,O,N)}catch(D){if(this._zoneDelegate.handleError(this,D))throw D}}finally{b=b.parent}}runTask(e,d,O){if(e.zone!=this)throw new Error("A task can only be run in the zone of creation! (Creation: "+(e.zone||J).name+"; Execution: "+this.name+")");const N=e,{type:D,data:{isPeriodic:_e=!1,isRefreshable:he=!1}={}}=e;if(e.state===X&&(D===W||D===p))return;const oe=e.state!=x;oe&&N._transitionTo(x,h);const ye=S;S=N,b={parent:b,zone:this};try{D==p&&e.data&&!_e&&!he&&(e.cancelFn=void 0);try{return this._zoneDelegate.invokeTask(this,N,d,O)}catch(l){if(this._zoneDelegate.handleError(this,l))throw l}}finally{const l=e.state;if(l!==X&&l!==Y)if(D==W||_e||he&&l===k)oe&&N._transitionTo(h,x,k);else{const a=N._zoneDelegates;this._updateTaskCount(N,-1),oe&&N._transitionTo(X,x,X),he&&(N._zoneDelegates=a)}b=b.parent,S=ye}}scheduleTask(e){if(e.zone&&e.zone!==this){let O=this;for(;O;){if(O===e.zone)throw Error(`can not reschedule task to ${this.name} which is descendants of the original zone ${e.zone.name}`);O=O.parent}}e._transitionTo(k,X);const d=[];e._zoneDelegates=d,e._zone=this;try{e=this._zoneDelegate.scheduleTask(this,e)}catch(O){throw e._transitionTo(Y,k,X),this._zoneDelegate.handleError(this,O),O}return e._zoneDelegates===d&&this._updateTaskCount(e,1),e.state==k&&e._transitionTo(h,k),e}scheduleMicroTask(e,d,O,N){return this.scheduleTask(new T(B,e,d,O,N,void 0))}scheduleMacroTask(e,d,O,N,D){return this.scheduleTask(new T(p,e,d,O,N,D))}scheduleEventTask(e,d,O,N,D){return this.scheduleTask(new T(W,e,d,O,N,D))}cancelTask(e){if(e.zone!=this)throw new Error("A task can only be cancelled in the zone of creation! (Creation: "+(e.zone||J).name+"; Execution: "+this.name+")");if(e.state===h||e.state===x){e._transitionTo(G,h,x);try{this._zoneDelegate.cancelTask(this,e)}catch(d){throw e._transitionTo(Y,G),this._zoneDelegate.handleError(this,d),d}return this._updateTaskCount(e,-1),e._transitionTo(X,G),e.runCount=-1,e}}_updateTaskCount(e,d){const O=e._zoneDelegates;-1==d&&(e._zoneDelegates=null);for(let N=0;Nthis.__symbol__=Q}return j(),_})();const s={name:"",onHasTask:(j,_,c,e)=>j.hasTask(c,e),onScheduleTask:(j,_,c,e)=>j.scheduleTask(c,e),onInvokeTask:(j,_,c,e,d,O)=>j.invokeTask(c,e,d,O),onCancelTask:(j,_,c,e)=>j.cancelTask(c,e)};class f{get zone(){return this._zone}constructor(_,c,e){this._taskCounts={microTask:0,macroTask:0,eventTask:0},this._zone=_,this._parentDelegate=c,this._forkZS=e&&(e&&e.onFork?e:c._forkZS),this._forkDlgt=e&&(e.onFork?c:c._forkDlgt),this._forkCurrZone=e&&(e.onFork?this._zone:c._forkCurrZone),this._interceptZS=e&&(e.onIntercept?e:c._interceptZS),this._interceptDlgt=e&&(e.onIntercept?c:c._interceptDlgt),this._interceptCurrZone=e&&(e.onIntercept?this._zone:c._interceptCurrZone),this._invokeZS=e&&(e.onInvoke?e:c._invokeZS),this._invokeDlgt=e&&(e.onInvoke?c:c._invokeDlgt),this._invokeCurrZone=e&&(e.onInvoke?this._zone:c._invokeCurrZone),this._handleErrorZS=e&&(e.onHandleError?e:c._handleErrorZS),this._handleErrorDlgt=e&&(e.onHandleError?c:c._handleErrorDlgt),this._handleErrorCurrZone=e&&(e.onHandleError?this._zone:c._handleErrorCurrZone),this._scheduleTaskZS=e&&(e.onScheduleTask?e:c._scheduleTaskZS),this._scheduleTaskDlgt=e&&(e.onScheduleTask?c:c._scheduleTaskDlgt),this._scheduleTaskCurrZone=e&&(e.onScheduleTask?this._zone:c._scheduleTaskCurrZone),this._invokeTaskZS=e&&(e.onInvokeTask?e:c._invokeTaskZS),this._invokeTaskDlgt=e&&(e.onInvokeTask?c:c._invokeTaskDlgt),this._invokeTaskCurrZone=e&&(e.onInvokeTask?this._zone:c._invokeTaskCurrZone),this._cancelTaskZS=e&&(e.onCancelTask?e:c._cancelTaskZS),this._cancelTaskDlgt=e&&(e.onCancelTask?c:c._cancelTaskDlgt),this._cancelTaskCurrZone=e&&(e.onCancelTask?this._zone:c._cancelTaskCurrZone),this._hasTaskZS=null,this._hasTaskDlgt=null,this._hasTaskDlgtOwner=null,this._hasTaskCurrZone=null;const d=e&&e.onHasTask;(d||c&&c._hasTaskZS)&&(this._hasTaskZS=d?e:s,this._hasTaskDlgt=c,this._hasTaskDlgtOwner=this,this._hasTaskCurrZone=this._zone,e.onScheduleTask||(this._scheduleTaskZS=s,this._scheduleTaskDlgt=c,this._scheduleTaskCurrZone=this._zone),e.onInvokeTask||(this._invokeTaskZS=s,this._invokeTaskDlgt=c,this._invokeTaskCurrZone=this._zone),e.onCancelTask||(this._cancelTaskZS=s,this._cancelTaskDlgt=c,this._cancelTaskCurrZone=this._zone))}fork(_,c){return this._forkZS?this._forkZS.onFork(this._forkDlgt,this.zone,_,c):new n(_,c)}intercept(_,c,e){return this._interceptZS?this._interceptZS.onIntercept(this._interceptDlgt,this._interceptCurrZone,_,c,e):c}invoke(_,c,e,d,O){return this._invokeZS?this._invokeZS.onInvoke(this._invokeDlgt,this._invokeCurrZone,_,c,e,d,O):c.apply(e,d)}handleError(_,c){return!this._handleErrorZS||this._handleErrorZS.onHandleError(this._handleErrorDlgt,this._handleErrorCurrZone,_,c)}scheduleTask(_,c){let e=c;if(this._scheduleTaskZS)this._hasTaskZS&&e._zoneDelegates.push(this._hasTaskDlgtOwner),e=this._scheduleTaskZS.onScheduleTask(this._scheduleTaskDlgt,this._scheduleTaskCurrZone,_,c),e||(e=c);else if(c.scheduleFn)c.scheduleFn(c);else{if(c.type!=B)throw new Error("Task is missing scheduleFn.");z(c)}return e}invokeTask(_,c,e,d){return this._invokeTaskZS?this._invokeTaskZS.onInvokeTask(this._invokeTaskDlgt,this._invokeTaskCurrZone,_,c,e,d):c.callback.apply(e,d)}cancelTask(_,c){let e;if(this._cancelTaskZS)e=this._cancelTaskZS.onCancelTask(this._cancelTaskDlgt,this._cancelTaskCurrZone,_,c);else{if(!c.cancelFn)throw Error("Task is not cancelable");e=c.cancelFn(c)}return e}hasTask(_,c){try{this._hasTaskZS&&this._hasTaskZS.onHasTask(this._hasTaskDlgt,this._hasTaskCurrZone,_,c)}catch(e){this.handleError(_,e)}}_updateTaskCount(_,c){const e=this._taskCounts,d=e[_],O=e[_]=d+c;if(O<0)throw new Error("More tasks executed then were scheduled.");0!=d&&0!=O||this.hasTask(this._zone,{microTask:e.microTask>0,macroTask:e.macroTask>0,eventTask:e.eventTask>0,change:_})}}class T{constructor(_,c,e,d,O,N){if(this._zone=null,this.runCount=0,this._zoneDelegates=null,this._state="notScheduled",this.type=_,this.source=c,this.data=d,this.scheduleFn=O,this.cancelFn=N,!e)throw new Error("callback is not defined");this.callback=e;const D=this;this.invoke=_===W&&d&&d.useG?T.invokeTask:function(){return T.invokeTask.call(te,D,this,arguments)}}static invokeTask(_,c,e){_||(_=this),ee++;try{return _.runCount++,_.zone.runTask(_,c,e)}finally{1==ee&&K(),ee--}}get zone(){return this._zone}get state(){return this._state}cancelScheduleRequest(){this._transitionTo(X,k)}_transitionTo(_,c,e){if(this._state!==c&&this._state!==e)throw new Error(`${this.type} '${this.source}': can not transition to '${_}', expecting state '${c}'${e?" or '"+e+"'":""}, was '${this._state}'.`);this._state=_,_==X&&(this._zoneDelegates=null)}toString(){return this.data&&typeof this.data.handleId<"u"?this.data.handleId.toString():Object.prototype.toString.call(this)}toJSON(){return{type:this.type,state:this.state,source:this.source,zone:this.zone.name,runCount:this.runCount}}}const g=Q("setTimeout"),m=Q("Promise"),C=Q("then");let A,E=[],P=!1;function V(j){if(A||te[m]&&(A=te[m].resolve(0)),A){let _=A[C];_||(_=A.then),_.call(A,j)}else te[g](j,0)}function z(j){0===ee&&0===E.length&&V(K),j&&E.push(j)}function K(){if(!P){for(P=!0;E.length;){const j=E;E=[];for(let _=0;_b,onUnhandledError:q,microtaskDrainDone:q,scheduleMicroTask:z,showUncaughtError:()=>!n[Q("ignoreConsoleErrorUncaughtError")],patchEventTarget:()=>[],patchOnProperties:q,patchMethod:()=>q,bindArguments:()=>[],patchThen:()=>q,patchMacroTask:()=>q,patchEventPrototype:()=>q,isIEOrEdge:()=>!1,getGlobalObjects:()=>{},ObjectDefineProperty:()=>q,ObjectGetOwnPropertyDescriptor:()=>{},ObjectCreate:()=>{},ArraySlice:()=>[],patchClass:()=>q,wrapWithCurrentZone:()=>q,filterProperties:()=>[],attachOriginToPatched:()=>q,_redefineProperty:()=>q,patchCallbacks:()=>q,nativeScheduleMicroTask:V};let b={parent:null,zone:new n(null,null)},S=null,ee=0;function q(){}return i("Zone","Zone"),n}(),t.Zone}();(function Zt(t){(function Nt(t){t.__load_patch("ZoneAwarePromise",(r,i,n)=>{const s=Object.getOwnPropertyDescriptor,f=Object.defineProperty,g=n.symbol,m=[],C=!1!==r[g("DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION")],E=g("Promise"),P=g("then");n.onUnhandledError=l=>{if(n.showUncaughtError()){const a=l&&l.rejection;a?console.error("Unhandled Promise rejection:",a instanceof Error?a.message:a,"; Zone:",l.zone.name,"; Task:",l.task&&l.task.source,"; Value:",a,a instanceof Error?a.stack:void 0):console.error(l)}},n.microtaskDrainDone=()=>{for(;m.length;){const l=m.shift();try{l.zone.runGuarded(()=>{throw l.throwOriginal?l.rejection:l})}catch(a){z(a)}}};const V=g("unhandledPromiseRejectionHandler");function z(l){n.onUnhandledError(l);try{const a=i[V];"function"==typeof a&&a.call(this,l)}catch{}}function K(l){return l&&l.then}function J(l){return l}function X(l){return D.reject(l)}const k=g("state"),h=g("value"),x=g("finally"),G=g("parentPromiseValue"),Y=g("parentPromiseState"),p=null,W=!0,L=!1;function b(l,a){return o=>{try{j(l,a,o)}catch(u){j(l,!1,u)}}}const S=function(){let l=!1;return function(o){return function(){l||(l=!0,o.apply(null,arguments))}}},ee="Promise resolved with itself",q=g("currentTaskTrace");function j(l,a,o){const u=S();if(l===o)throw new TypeError(ee);if(l[k]===p){let v=null;try{("object"==typeof o||"function"==typeof o)&&(v=o&&o.then)}catch(R){return u(()=>{j(l,!1,R)})(),l}if(a!==L&&o instanceof D&&o.hasOwnProperty(k)&&o.hasOwnProperty(h)&&o[k]!==p)c(o),j(l,o[k],o[h]);else if(a!==L&&"function"==typeof v)try{v.call(o,u(b(l,a)),u(b(l,!1)))}catch(R){u(()=>{j(l,!1,R)})()}else{l[k]=a;const R=l[h];if(l[h]=o,l[x]===x&&a===W&&(l[k]=l[Y],l[h]=l[G]),a===L&&o instanceof Error){const y=i.currentTask&&i.currentTask.data&&i.currentTask.data.__creationTrace__;y&&f(o,q,{configurable:!0,enumerable:!1,writable:!0,value:y})}for(let y=0;y{try{const I=l[h],M=!!o&&x===o[x];M&&(o[G]=I,o[Y]=R);const Z=a.run(y,void 0,M&&y!==X&&y!==J?[]:[I]);j(o,!0,Z)}catch(I){j(o,!1,I)}},o)}const O=function(){},N=r.AggregateError;class D{static toString(){return"function ZoneAwarePromise() { [native code] }"}static resolve(a){return a instanceof D?a:j(new this(null),W,a)}static reject(a){return j(new this(null),L,a)}static withResolvers(){const a={};return a.promise=new D((o,u)=>{a.resolve=o,a.reject=u}),a}static any(a){if(!a||"function"!=typeof a[Symbol.iterator])return Promise.reject(new N([],"All promises were rejected"));const o=[];let u=0;try{for(let y of a)u++,o.push(D.resolve(y))}catch{return Promise.reject(new N([],"All promises were rejected"))}if(0===u)return Promise.reject(new N([],"All promises were rejected"));let v=!1;const R=[];return new D((y,I)=>{for(let M=0;M{v||(v=!0,y(Z))},Z=>{R.push(Z),u--,0===u&&(v=!0,I(new N(R,"All promises were rejected")))})})}static race(a){let o,u,v=new this((I,M)=>{o=I,u=M});function R(I){o(I)}function y(I){u(I)}for(let I of a)K(I)||(I=this.resolve(I)),I.then(R,y);return v}static all(a){return D.allWithCallback(a)}static allSettled(a){return(this&&this.prototype instanceof D?this:D).allWithCallback(a,{thenCallback:u=>({status:"fulfilled",value:u}),errorCallback:u=>({status:"rejected",reason:u})})}static allWithCallback(a,o){let u,v,R=new this((Z,F)=>{u=Z,v=F}),y=2,I=0;const M=[];for(let Z of a){K(Z)||(Z=this.resolve(Z));const F=I;try{Z.then(U=>{M[F]=o?o.thenCallback(U):U,y--,0===y&&u(M)},U=>{o?(M[F]=o.errorCallback(U),y--,0===y&&u(M)):v(U)})}catch(U){v(U)}y++,I++}return y-=2,0===y&&u(M),R}constructor(a){const o=this;if(!(o instanceof D))throw new Error("Must be an instanceof Promise.");o[k]=p,o[h]=[];try{const u=S();a&&a(u(b(o,W)),u(b(o,L)))}catch(u){j(o,!1,u)}}get[Symbol.toStringTag](){return"Promise"}get[Symbol.species](){return D}then(a,o){let u=this.constructor?.[Symbol.species];(!u||"function"!=typeof u)&&(u=this.constructor||D);const v=new u(O),R=i.current;return this[k]==p?this[h].push(R,v,a,o):e(this,R,v,a,o),v}catch(a){return this.then(null,a)}finally(a){let o=this.constructor?.[Symbol.species];(!o||"function"!=typeof o)&&(o=D);const u=new o(O);u[x]=x;const v=i.current;return this[k]==p?this[h].push(v,u,a,a):e(this,v,u,a,a),u}}D.resolve=D.resolve,D.reject=D.reject,D.race=D.race,D.all=D.all;const _e=r[E]=r.Promise;r.Promise=D;const he=g("thenPatched");function oe(l){const a=l.prototype,o=s(a,"then");if(o&&(!1===o.writable||!o.configurable))return;const u=a.then;a[P]=u,l.prototype.then=function(v,R){return new D((I,M)=>{u.call(this,I,M)}).then(v,R)},l[he]=!0}return n.patchThen=oe,_e&&(oe(_e),ue(r,"fetch",l=>function ye(l){return function(a,o){let u=l.apply(a,o);if(u instanceof D)return u;let v=u.constructor;return v[he]||oe(v),u}}(l))),Promise[i.__symbol__("uncaughtPromiseErrors")]=m,D})})(t),function Lt(t){t.__load_patch("toString",r=>{const i=Function.prototype.toString,n=H("OriginalDelegate"),s=H("Promise"),f=H("Error"),T=function(){if("function"==typeof this){const E=this[n];if(E)return"function"==typeof E?i.call(E):Object.prototype.toString.call(E);if(this===Promise){const P=r[s];if(P)return i.call(P)}if(this===Error){const P=r[f];if(P)return i.call(P)}}return i.call(this)};T[n]=i,Function.prototype.toString=T;const g=Object.prototype.toString;Object.prototype.toString=function(){return"function"==typeof Promise&&this instanceof Promise?"[object Promise]":g.call(this)}})}(t),function Mt(t){t.__load_patch("util",(r,i,n)=>{const s=Fe(r);n.patchOnProperties=Ke,n.patchMethod=ue,n.bindArguments=Ve,n.patchMacroTask=yt;const f=i.__symbol__("BLACK_LISTED_EVENTS"),T=i.__symbol__("UNPATCHED_EVENTS");r[T]&&(r[f]=r[T]),r[f]&&(i[f]=i[T]=r[f]),n.patchEventPrototype=Pt,n.patchEventTarget=bt,n.isIEOrEdge=kt,n.ObjectDefineProperty=Le,n.ObjectGetOwnPropertyDescriptor=Ee,n.ObjectCreate=_t,n.ArraySlice=Et,n.patchClass=we,n.wrapWithCurrentZone=He,n.filterProperties=it,n.attachOriginToPatched=fe,n._redefineProperty=Object.defineProperty,n.patchCallbacks=It,n.getGlobalObjects=()=>({globalSources:tt,zoneSymbolEventNames:ne,eventNames:s,isBrowser:Ge,isMix:Xe,isNode:De,TRUE_STR:ae,FALSE_STR:le,ZONE_SYMBOL_PREFIX:Pe,ADD_EVENT_LISTENER_STR:Me,REMOVE_EVENT_LISTENER_STR:Ze})})}(t)})(at),function Ot(t){t.__load_patch("legacy",r=>{const i=r[t.__symbol__("legacyPatch")];i&&i()}),t.__load_patch("timers",r=>{const n="clear";pe(r,"set",n,"Timeout"),pe(r,"set",n,"Interval"),pe(r,"set",n,"Immediate")}),t.__load_patch("requestAnimationFrame",r=>{pe(r,"request","cancel","AnimationFrame"),pe(r,"mozRequest","mozCancel","AnimationFrame"),pe(r,"webkitRequest","webkitCancel","AnimationFrame")}),t.__load_patch("blocking",(r,i)=>{const n=["alert","prompt","confirm"];for(let s=0;sfunction(C,E){return i.current.run(T,r,E,m)})}),t.__load_patch("EventTarget",(r,i,n)=>{(function Dt(t,r){r.patchEventPrototype(t,r)})(r,n),function Ct(t,r){if(Zone[r.symbol("patchEventTarget")])return;const{eventNames:i,zoneSymbolEventNames:n,TRUE_STR:s,FALSE_STR:f,ZONE_SYMBOL_PREFIX:T}=r.getGlobalObjects();for(let m=0;m{we("MutationObserver"),we("WebKitMutationObserver")}),t.__load_patch("IntersectionObserver",(r,i,n)=>{we("IntersectionObserver")}),t.__load_patch("FileReader",(r,i,n)=>{we("FileReader")}),t.__load_patch("on_property",(r,i,n)=>{!function St(t,r){if(De&&!Xe||Zone[t.symbol("patchEvents")])return;const i=r.__Zone_ignore_on_properties;let n=[];if(Ge){const s=window;n=n.concat(["Document","SVGElement","Element","HTMLElement","HTMLBodyElement","HTMLMediaElement","HTMLFrameSetElement","HTMLFrameElement","HTMLIFrameElement","HTMLMarqueeElement","Worker"]);const f=function mt(){try{const t=Te.navigator.userAgent;if(-1!==t.indexOf("MSIE ")||-1!==t.indexOf("Trident/"))return!0}catch{}return!1}()?[{target:s,ignoreProperties:["error"]}]:[];ct(s,Fe(s),i&&i.concat(f),Ie(s))}n=n.concat(["XMLHttpRequest","XMLHttpRequestEventTarget","IDBIndex","IDBRequest","IDBOpenDBRequest","IDBDatabase","IDBTransaction","IDBCursor","WebSocket"]);for(let s=0;s{!function Rt(t,r){const{isBrowser:i,isMix:n}=r.getGlobalObjects();(i||n)&&t.customElements&&"customElements"in t&&r.patchCallbacks(r,t.customElements,"customElements","define",["connectedCallback","disconnectedCallback","adoptedCallback","attributeChangedCallback","formAssociatedCallback","formDisabledCallback","formResetCallback","formStateRestoreCallback"])}(r,n)}),t.__load_patch("XHR",(r,i)=>{!function C(E){const P=E.XMLHttpRequest;if(!P)return;const A=P.prototype;let z=A[Ae],K=A[je];if(!z){const w=E.XMLHttpRequestEventTarget;if(w){const b=w.prototype;z=b[Ae],K=b[je]}}const J="readystatechange",X="scheduled";function k(w){const b=w.data,S=b.target;S[T]=!1,S[m]=!1;const ee=S[f];z||(z=S[Ae],K=S[je]),ee&&K.call(S,J,ee);const q=S[f]=()=>{if(S.readyState===S.DONE)if(!b.aborted&&S[T]&&w.state===X){const _=S[i.__symbol__("loadfalse")];if(0!==S.status&&_&&_.length>0){const c=w.invoke;w.invoke=function(){const e=S[i.__symbol__("loadfalse")];for(let d=0;dfunction(w,b){return w[s]=0==b[2],w[g]=b[1],G.apply(w,b)}),B=H("fetchTaskAborting"),p=H("fetchTaskScheduling"),W=ue(A,"send",()=>function(w,b){if(!0===i.current[p]||w[s])return W.apply(w,b);{const S={target:w,url:w[g],isPeriodic:!1,args:b,aborted:!1},ee=xe("XMLHttpRequest.send",h,S,k,x);w&&!0===w[m]&&!S.aborted&&ee.state===X&&ee.invoke()}}),L=ue(A,"abort",()=>function(w,b){const S=function V(w){return w[n]}(w);if(S&&"string"==typeof S.type){if(null==S.cancelFn||S.data&&S.data.aborted)return;S.zone.cancelTask(S)}else if(!0===i.current[B])return L.apply(w,b)})}(r);const n=H("xhrTask"),s=H("xhrSync"),f=H("xhrListener"),T=H("xhrScheduled"),g=H("xhrURL"),m=H("xhrErrorBeforeScheduled")}),t.__load_patch("geolocation",r=>{r.navigator&&r.navigator.geolocation&&function gt(t,r){const i=t.constructor.name;for(let n=0;n{const m=function(){return g.apply(this,Ve(arguments,i+"."+s))};return fe(m,g),m})(f)}}}(r.navigator.geolocation,["getCurrentPosition","watchPosition"])}),t.__load_patch("PromiseRejectionEvent",(r,i)=>{function n(s){return function(f){st(r,s).forEach(g=>{const m=r.PromiseRejectionEvent;if(m){const C=new m(s,{promise:f.promise,reason:f.rejection});g.invoke(C)}})}}r.PromiseRejectionEvent&&(i[H("unhandledPromiseRejectionHandler")]=n("unhandledrejection"),i[H("rejectionHandledHandler")]=n("rejectionhandled"))}),t.__load_patch("queueMicrotask",(r,i,n)=>{!function wt(t,r){r.patchMethod(t,"queueMicrotask",i=>function(n,s){Zone.current.scheduleMicroTask("queueMicrotask",s[0])})}(r,n)})}(at)}},te=>{te(te.s=50)}]); + +"use strict";(self.webpackChunkcoverage_app=self.webpackChunkcoverage_app||[]).push([[792],{653:()=>{let Yo;function sr(){return Yo}function un(e){const n=Yo;return Yo=e,n}const dI=Symbol("NotFound");function Vc(e){return e===dI||"\u0275NotFound"===e?.name}Error;let Je=null,ar=!1,Hc=1;const We=Symbol("SIGNAL");function z(e){const n=Je;return Je=e,n}const Qo={version:0,lastCleanEpoch:0,dirty:!1,producers:void 0,producersTail:void 0,consumers:void 0,consumersTail:void 0,recomputing:!1,consumerAllowSignalWrites:!1,consumerIsAlwaysLive:!1,kind:"unknown",producerMustRecompute:()=>!1,producerRecomputeValue:()=>{},consumerMarkedDirty:()=>{},consumerOnSignalRead:()=>{}};function Vs(e){if(ar)throw new Error("");if(null===Je)return;Je.consumerOnSignalRead(e);const n=Je.producersTail;if(void 0!==n&&n.producer===e)return;let t;const o=Je.recomputing;if(o&&(t=void 0!==n?n.nextProducer:Je.producers,void 0!==t&&t.producer===e))return Je.producersTail=t,void(t.lastReadVersion=e.version);const i=e.consumersTail;if(void 0!==i&&i.consumer===Je&&(!o||function mI(e,n){const t=n.producersTail;if(void 0!==t){let o=n.producers;do{if(o===e)return!0;if(o===t)break;o=o.nextProducer}while(void 0!==o)}return!1}(i,Je)))return;const r=Jo(Je),s={producer:e,consumer:Je,nextProducer:t,prevConsumer:i,lastReadVersion:e.version,nextConsumer:void 0};Je.producersTail=s,void 0!==n?n.nextProducer=s:Je.producers=s,r&&Gg(e,s)}function lr(e){if((!Jo(e)||e.dirty)&&(e.dirty||e.lastCleanEpoch!==Hc)){if(!e.producerMustRecompute(e)&&!Bs(e))return void Hs(e);e.producerRecomputeValue(e),Hs(e)}}function $g(e){if(void 0===e.consumers)return;const n=ar;ar=!0;try{for(let t=e.consumers;void 0!==t;t=t.nextConsumer){const o=t.consumer;o.dirty||hI(o)}}finally{ar=n}}function zg(){return!1!==Je?.consumerAllowSignalWrites}function hI(e){e.dirty=!0,$g(e),e.consumerMarkedDirty?.(e)}function Hs(e){e.dirty=!1,e.lastCleanEpoch=Hc}function Ko(e){return e&&function gI(e){e.producersTail=void 0,e.recomputing=!0}(e),z(e)}function cr(e,n){z(n),e&&function pI(e){e.recomputing=!1;const n=e.producersTail;let t=void 0!==n?n.nextProducer:e.producers;if(void 0!==t){if(Jo(e))do{t=Uc(t)}while(void 0!==t);void 0!==n?n.nextProducer=void 0:e.producers=void 0}}(e)}function Bs(e){for(let n=e.producers;void 0!==n;n=n.nextProducer){const t=n.producer,o=n.lastReadVersion;if(o!==t.version||(lr(t),o!==t.version))return!0}return!1}function ur(e){if(Jo(e)){let n=e.producers;for(;void 0!==n;)n=Uc(n)}e.producers=void 0,e.producersTail=void 0,e.consumers=void 0,e.consumersTail=void 0}function Gg(e,n){const t=e.consumersTail,o=Jo(e);if(void 0!==t?(n.nextConsumer=t.nextConsumer,t.nextConsumer=n):(n.nextConsumer=void 0,e.consumers=n),n.prevConsumer=t,e.consumersTail=n,!o)for(let i=e.producers;void 0!==i;i=i.nextProducer)Gg(i.producer,i)}function Uc(e){const n=e.producer,t=e.nextProducer,o=e.nextConsumer,i=e.prevConsumer;if(e.nextConsumer=void 0,e.prevConsumer=void 0,void 0!==o?o.prevConsumer=i:n.consumersTail=i,void 0!==i)i.nextConsumer=o;else if(n.consumers=o,!Jo(n)){let r=n.producers;for(;void 0!==r;)r=Uc(r)}return t}function Jo(e){return e.consumerIsAlwaysLive||void 0!==e.consumers}function zc(e,n){return Object.is(e,n)}const lo=Symbol("UNSET"),Xo=Symbol("COMPUTING"),Rn=Symbol("ERRORED"),vI={...Qo,value:lo,dirty:!0,error:null,equal:zc,kind:"computed",producerMustRecompute:e=>e.value===lo||e.value===Xo,producerRecomputeValue(e){if(e.value===Xo)throw new Error("");const n=e.value;e.value=Xo;const t=Ko(e);let o,i=!1;try{o=e.computation(),z(null),i=n!==lo&&n!==Rn&&o!==Rn&&e.equal(n,o)}catch(r){o=Rn,e.error=r}finally{cr(e,t)}i?e.value=n:(e.value=o,e.version++)}};let Wg=function yI(){throw new Error};function qg(e){Wg(e)}function bI(e,n){const t=Object.create(Yg);t.value=e,void 0!==n&&(t.equal=n);const o=()=>function DI(e){return Vs(e),e.value}(t);return o[We]=t,[o,s=>Gc(t,s),s=>function Zg(e,n){zg()||qg(e),Gc(e,n(e.value))}(t,s)]}function Gc(e,n){zg()||qg(e),e.equal(e.value,n)||(e.value=n,function wI(e){e.version++,function fI(){Hc++}(),$g(e)}(e))}const Yg={...Qo,equal:zc,value:void 0,kind:"signal"};function ke(e){return"function"==typeof e}function Qg(e){const t=e(o=>{Error.call(o),o.stack=(new Error).stack});return t.prototype=Object.create(Error.prototype),t.prototype.constructor=t,t}const Wc=Qg(e=>function(t){e(this),this.message=t?`${t.length} errors occurred during unsubscription:\n${t.map((o,i)=>`${i+1}) ${o.toString()}`).join("\n ")}`:"",this.name="UnsubscriptionError",this.errors=t});function Us(e,n){if(e){const t=e.indexOf(n);0<=t&&e.splice(t,1)}}class It{constructor(n){this.initialTeardown=n,this.closed=!1,this._parentage=null,this._finalizers=null}unsubscribe(){let n;if(!this.closed){this.closed=!0;const{_parentage:t}=this;if(t)if(this._parentage=null,Array.isArray(t))for(const r of t)r.remove(this);else t.remove(this);const{initialTeardown:o}=this;if(ke(o))try{o()}catch(r){n=r instanceof Wc?r.errors:[r]}const{_finalizers:i}=this;if(i){this._finalizers=null;for(const r of i)try{Xg(r)}catch(s){n=n??[],s instanceof Wc?n=[...n,...s.errors]:n.push(s)}}if(n)throw new Wc(n)}}add(n){var t;if(n&&n!==this)if(this.closed)Xg(n);else{if(n instanceof It){if(n.closed||n._hasParent(this))return;n._addParent(this)}(this._finalizers=null!==(t=this._finalizers)&&void 0!==t?t:[]).push(n)}}_hasParent(n){const{_parentage:t}=this;return t===n||Array.isArray(t)&&t.includes(n)}_addParent(n){const{_parentage:t}=this;this._parentage=Array.isArray(t)?(t.push(n),t):t?[t,n]:n}_removeParent(n){const{_parentage:t}=this;t===n?this._parentage=null:Array.isArray(t)&&Us(t,n)}remove(n){const{_finalizers:t}=this;t&&Us(t,n),n instanceof It&&n._removeParent(this)}}It.EMPTY=(()=>{const e=new It;return e.closed=!0,e})();const Kg=It.EMPTY;function Jg(e){return e instanceof It||e&&"closed"in e&&ke(e.remove)&&ke(e.add)&&ke(e.unsubscribe)}function Xg(e){ke(e)?e():e.unsubscribe()}const co={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1},$s={setTimeout(e,n,...t){const{delegate:o}=$s;return o?.setTimeout?o.setTimeout(e,n,...t):setTimeout(e,n,...t)},clearTimeout(e){const{delegate:n}=$s;return(n?.clearTimeout||clearTimeout)(e)},delegate:void 0};function ep(e){$s.setTimeout(()=>{const{onUnhandledError:n}=co;if(!n)throw e;n(e)})}function tp(){}const EI=qc("C",void 0,void 0);function qc(e,n,t){return{kind:e,value:n,error:t}}let uo=null;function zs(e){if(co.useDeprecatedSynchronousErrorHandling){const n=!uo;if(n&&(uo={errorThrown:!1,error:null}),e(),n){const{errorThrown:t,error:o}=uo;if(uo=null,t)throw o}}else e()}class Zc extends It{constructor(n){super(),this.isStopped=!1,n?(this.destination=n,Jg(n)&&n.add(this)):this.destination=OI}static create(n,t,o){return new Qc(n,t,o)}next(n){this.isStopped?Kc(function II(e){return qc("N",e,void 0)}(n),this):this._next(n)}error(n){this.isStopped?Kc(function MI(e){return qc("E",void 0,e)}(n),this):(this.isStopped=!0,this._error(n))}complete(){this.isStopped?Kc(EI,this):(this.isStopped=!0,this._complete())}unsubscribe(){this.closed||(this.isStopped=!0,super.unsubscribe(),this.destination=null)}_next(n){this.destination.next(n)}_error(n){try{this.destination.error(n)}finally{this.unsubscribe()}}_complete(){try{this.destination.complete()}finally{this.unsubscribe()}}}const SI=Function.prototype.bind;function Yc(e,n){return SI.call(e,n)}class AI{constructor(n){this.partialObserver=n}next(n){const{partialObserver:t}=this;if(t.next)try{t.next(n)}catch(o){Gs(o)}}error(n){const{partialObserver:t}=this;if(t.error)try{t.error(n)}catch(o){Gs(o)}else Gs(n)}complete(){const{partialObserver:n}=this;if(n.complete)try{n.complete()}catch(t){Gs(t)}}}class Qc extends Zc{constructor(n,t,o){let i;if(super(),ke(n)||!n)i={next:n??void 0,error:t??void 0,complete:o??void 0};else{let r;this&&co.useDeprecatedNextContext?(r=Object.create(n),r.unsubscribe=()=>this.unsubscribe(),i={next:n.next&&Yc(n.next,r),error:n.error&&Yc(n.error,r),complete:n.complete&&Yc(n.complete,r)}):i=n}this.destination=new AI(i)}}function Gs(e){co.useDeprecatedSynchronousErrorHandling?function TI(e){co.useDeprecatedSynchronousErrorHandling&&uo&&(uo.errorThrown=!0,uo.error=e)}(e):ep(e)}function Kc(e,n){const{onStoppedNotification:t}=co;t&&$s.setTimeout(()=>t(e,n))}const OI={closed:!0,next:tp,error:function NI(e){throw e},complete:tp},Jc="function"==typeof Symbol&&Symbol.observable||"@@observable";function Xc(e){return e}let ht=(()=>{class e{constructor(t){t&&(this._subscribe=t)}lift(t){const o=new e;return o.source=this,o.operator=t,o}subscribe(t,o,i){const r=function RI(e){return e&&e instanceof Zc||function xI(e){return e&&ke(e.next)&&ke(e.error)&&ke(e.complete)}(e)&&Jg(e)}(t)?t:new Qc(t,o,i);return zs(()=>{const{operator:s,source:a}=this;r.add(s?s.call(r,a):a?this._subscribe(r):this._trySubscribe(r))}),r}_trySubscribe(t){try{return this._subscribe(t)}catch(o){t.error(o)}}forEach(t,o){return new(o=op(o))((i,r)=>{const s=new Qc({next:a=>{try{t(a)}catch(l){r(l),s.unsubscribe()}},error:r,complete:i});this.subscribe(s)})}_subscribe(t){var o;return null===(o=this.source)||void 0===o?void 0:o.subscribe(t)}[Jc](){return this}pipe(...t){return function np(e){return 0===e.length?Xc:1===e.length?e[0]:function(t){return e.reduce((o,i)=>i(o),t)}}(t)(this)}toPromise(t){return new(t=op(t))((o,i)=>{let r;this.subscribe(s=>r=s,s=>i(s),()=>o(r))})}}return e.create=n=>new e(n),e})();function op(e){var n;return null!==(n=e??co.Promise)&&void 0!==n?n:Promise}const kI=Qg(e=>function(){e(this),this.name="ObjectUnsubscribedError",this.message="object unsubscribed"});let Xt=(()=>{class e extends ht{constructor(){super(),this.closed=!1,this.currentObservers=null,this.observers=[],this.isStopped=!1,this.hasError=!1,this.thrownError=null}lift(t){const o=new ip(this,this);return o.operator=t,o}_throwIfClosed(){if(this.closed)throw new kI}next(t){zs(()=>{if(this._throwIfClosed(),!this.isStopped){this.currentObservers||(this.currentObservers=Array.from(this.observers));for(const o of this.currentObservers)o.next(t)}})}error(t){zs(()=>{if(this._throwIfClosed(),!this.isStopped){this.hasError=this.isStopped=!0,this.thrownError=t;const{observers:o}=this;for(;o.length;)o.shift().error(t)}})}complete(){zs(()=>{if(this._throwIfClosed(),!this.isStopped){this.isStopped=!0;const{observers:t}=this;for(;t.length;)t.shift().complete()}})}unsubscribe(){this.isStopped=this.closed=!0,this.observers=this.currentObservers=null}get observed(){var t;return(null===(t=this.observers)||void 0===t?void 0:t.length)>0}_trySubscribe(t){return this._throwIfClosed(),super._trySubscribe(t)}_subscribe(t){return this._throwIfClosed(),this._checkFinalizedStatuses(t),this._innerSubscribe(t)}_innerSubscribe(t){const{hasError:o,isStopped:i,observers:r}=this;return o||i?Kg:(this.currentObservers=null,r.push(t),new It(()=>{this.currentObservers=null,Us(r,t)}))}_checkFinalizedStatuses(t){const{hasError:o,thrownError:i,isStopped:r}=this;o?t.error(i):r&&t.complete()}asObservable(){const t=new ht;return t.source=this,t}}return e.create=(n,t)=>new ip(n,t),e})();class ip extends Xt{constructor(n,t){super(),this.destination=n,this.source=t}next(n){var t,o;null===(o=null===(t=this.destination)||void 0===t?void 0:t.next)||void 0===o||o.call(t,n)}error(n){var t,o;null===(o=null===(t=this.destination)||void 0===t?void 0:t.error)||void 0===o||o.call(t,n)}complete(){var n,t;null===(t=null===(n=this.destination)||void 0===n?void 0:n.complete)||void 0===t||t.call(n)}_subscribe(n){var t,o;return null!==(o=null===(t=this.source)||void 0===t?void 0:t.subscribe(n))&&void 0!==o?o:Kg}}class FI extends Xt{constructor(n){super(),this._value=n}get value(){return this.getValue()}_subscribe(n){const t=super._subscribe(n);return!t.closed&&n.next(this._value),t}getValue(){const{hasError:n,thrownError:t,_value:o}=this;if(n)throw t;return this._throwIfClosed(),o}next(n){super.next(this._value=n)}}const sp="https://angular.dev/best-practices/security#preventing-cross-site-scripting-xss";class T extends Error{code;constructor(n,t){super(function en(e,n){return`${function LI(e){return`NG0${Math.abs(e)}`}(e)}${n?": "+n:""}`}(n,t)),this.code=n}}const Ie=globalThis;function se(e){for(let n in e)if(e[n]===se)return n;throw Error("")}function PI(e,n){for(const t in n)n.hasOwnProperty(t)&&!e.hasOwnProperty(t)&&(e[t]=n[t])}function gt(e){if("string"==typeof e)return e;if(Array.isArray(e))return`[${e.map(gt).join(", ")}]`;if(null==e)return""+e;const n=e.overriddenName||e.name;if(n)return`${n}`;const t=e.toString();if(null==t)return""+t;const o=t.indexOf("\n");return o>=0?t.slice(0,o):t}function tu(e,n){return e?n?`${e} ${n}`:e:n||""}const VI=se({__forward_ref__:se});function ge(e){return e.__forward_ref__=ge,e.toString=function(){return gt(this())},e}function Q(e){return Ws(e)?e():e}function Ws(e){return"function"==typeof e&&e.hasOwnProperty(VI)&&e.__forward_ref__===ge}function X(e){return{token:e.token,providedIn:e.providedIn||null,factory:e.factory,value:void 0}}function dn(e){return{providers:e.providers||[],imports:e.imports||[]}}function qs(e){return function zI(e,n){return e.hasOwnProperty(n)&&e[n]||null}(e,Ys)}function Zs(e){return e&&e.hasOwnProperty(nu)?e[nu]:null}const Ys=se({\u0275prov:se}),nu=se({\u0275inj:se});class R{_desc;ngMetadataName="InjectionToken";\u0275prov;constructor(n,t){this._desc=n,this.\u0275prov=void 0,"number"==typeof t?this.__NG_ELEMENT_ID__=t:void 0!==t&&(this.\u0275prov=X({token:this,providedIn:t.providedIn||"root",factory:t.factory}))}get multi(){return this}toString(){return`InjectionToken ${this._desc}`}}function iu(e){return e&&!!e.\u0275providers}const ru=se({\u0275cmp:se}),QI=se({\u0275dir:se}),KI=se({\u0275pipe:se}),ap=se({\u0275mod:se}),ho=se({\u0275fac:se}),hr=se({__NG_ELEMENT_ID__:se}),lp=se({__NG_ENV_ID__:se});function Z(e){return"string"==typeof e?e:null==e?"":String(e)}const su=se({ngErrorCode:se}),cp=se({ngErrorMessage:se}),gr=se({ngTokenPath:se});function au(e,n){return up("",-200,n)}function lu(e,n){throw new T(-201,!1)}function up(e,n,t){const o=new T(n,e);return o[su]=n,o[cp]=e,t&&(o[gr]=t),o}let cu;function dp(){return cu}function pt(e){const n=cu;return cu=e,n}function fp(e,n,t){const o=qs(e);return o&&"root"==o.providedIn?void 0===o.value?o.value=o.factory():o.value:8&t?null:void 0!==n?n:void lu()}const go={};class oT{injector;constructor(n){this.injector=n}retrieve(n,t){const o=pr(t)||0;try{return this.injector.get(n,8&o?null:go,o)}catch(i){if(Vc(i))return i;throw i}}}function iT(e,n=0){const t=sr();if(void 0===t)throw new T(-203,!1);if(null===t)return fp(e,void 0,n);{const o=function rT(e){return{optional:!!(8&e),host:!!(1&e),self:!!(2&e),skipSelf:!!(4&e)}}(n),i=t.retrieve(e,o);if(Vc(i)){if(o.optional)return null;throw i}return i}}function oe(e,n=0){return(dp()||iT)(Q(e),n)}function F(e,n){return oe(e,pr(n))}function pr(e){return typeof e>"u"||"number"==typeof e?e:0|(e.optional&&8)|(e.host&&1)|(e.self&&2)|(e.skipSelf&&4)}function du(e){const n=[];for(let t=0;tArray.isArray(t)?ei(t,n):n(t))}function gp(e,n,t){n>=e.length?e.push(t):e.splice(n,0,t)}function Ks(e,n){return n>=e.length-1?e.pop():e.splice(n,1)[0]}function Xs(e,n,t){let o=_r(e,n);return o>=0?e[1|o]=t:(o=~o,function mp(e,n,t,o){let i=e.length;if(i==n)e.push(t,o);else if(1===i)e.push(o,e[0]),e[0]=t;else{for(i--,e.push(e[i-1],e[i]);i>n;)e[i]=e[i-2],i--;e[n]=t,e[n+1]=o}}(e,o,n,t)),o}function fu(e,n){const t=_r(e,n);if(t>=0)return e[1|t]}function _r(e,n){return function lT(e,n,t){let o=0,i=e.length>>t;for(;i!==o;){const r=o+(i-o>>1),s=e[r<n?i=r:o=r+1}return~(i<{t.push(s)};return ei(n,s=>{const a=s;ta(a,r,[],o)&&(i||=[],i.push(a))}),void 0!==i&&yp(i,r),t}function yp(e,n){for(let t=0;t{n(r,o)})}}function ta(e,n,t,o){if(!(e=Q(e)))return!1;let i=null,r=Zs(e);const s=!r&&le(e);if(r||s){if(s&&!s.standalone)return!1;i=e}else{const l=e.ngModule;if(r=Zs(l),!r)return!1;i=l}const a=o.has(i);if(s){if(a)return!1;if(o.add(i),s.dependencies){const l="function"==typeof s.dependencies?s.dependencies():s.dependencies;for(const c of l)ta(c,n,t,o)}}else{if(!r)return!1;{if(null!=r.imports&&!a){let c;o.add(i);try{ei(r.imports,u=>{ta(u,n,t,o)&&(c||=[],c.push(u))})}finally{}void 0!==c&&yp(c,n)}if(!a){const c=po(i)||(()=>new i);n({provide:i,useFactory:c,deps:_e},i),n({provide:hu,useValue:i,multi:!0},i),n({provide:mo,useValue:()=>oe(i),multi:!0},i)}const l=r.providers;if(null!=l&&!a){const c=e;mu(l,u=>{n(u,c)})}}}return i!==e&&void 0!==e.providers}function mu(e,n){for(let t of e)iu(t)&&(t=t.\u0275providers),Array.isArray(t)?mu(t,n):n(t)}const dT=se({provide:String,useValue:se});function _u(e){return null!==e&&"object"==typeof e&&dT in e}function fn(e){return"function"==typeof e}const vu=new R(""),na={},wp={};let yu;function Cu(){return void 0===yu&&(yu=new ea),yu}class Lt{}class _o extends Lt{parent;source;scopes;records=new Map;_ngOnDestroyHooks=new Set;_onDestroyHooks=[];get destroyed(){return this._destroyed}_destroyed=!1;injectorDefTypes;constructor(n,t,o,i){super(),this.parent=t,this.source=o,this.scopes=i,Du(n,s=>this.processProvider(s)),this.records.set(_p,ti(void 0,this)),i.has("environment")&&this.records.set(Lt,ti(void 0,this));const r=this.records.get(vu);null!=r&&"string"==typeof r.value&&this.scopes.add(r.value),this.injectorDefTypes=new Set(this.get(hu,_e,{self:!0}))}retrieve(n,t){const o=pr(t)||0;try{return this.get(n,go,o)}catch(i){if(Vc(i))return i;throw i}}destroy(){yr(this),this._destroyed=!0;const n=z(null);try{for(const o of this._ngOnDestroyHooks)o.ngOnDestroy();const t=this._onDestroyHooks;this._onDestroyHooks=[];for(const o of t)o()}finally{this.records.clear(),this._ngOnDestroyHooks.clear(),this.injectorDefTypes.clear(),z(n)}}onDestroy(n){return yr(this),this._onDestroyHooks.push(n),()=>this.removeOnDestroy(n)}runInContext(n){yr(this);const t=un(this),o=pt(void 0);try{return n()}finally{un(t),pt(o)}}get(n,t=go,o){if(yr(this),n.hasOwnProperty(lp))return n[lp](this);const i=pr(o),s=un(this),a=pt(void 0);try{if(!(4&i)){let c=this.records.get(n);if(void 0===c){const u=function mT(e){return"function"==typeof e||"object"==typeof e&&"InjectionToken"===e.ngMetadataName}(n)&&qs(n);c=u&&this.injectableDefInScope(u)?ti(bu(n),na):null,this.records.set(n,c)}if(null!=c)return this.hydrate(n,c,i)}return(2&i?Cu():this.parent).get(n,t=8&i&&t===go?null:t)}catch(l){const c=function tT(e){return e[su]}(l);throw-200===c||-201===c?new T(c,null):l}finally{pt(a),un(s)}}resolveInjectorInitializers(){const n=z(null),t=un(this),o=pt(void 0);try{const r=this.get(mo,_e,{self:!0});for(const s of r)s()}finally{un(t),pt(o),z(n)}}toString(){const n=[],t=this.records;for(const o of t.keys())n.push(gt(o));return`R3Injector[${n.join(", ")}]`}processProvider(n){let t=fn(n=Q(n))?n:Q(n&&n.provide);const o=function hT(e){return _u(e)?ti(void 0,e.useValue):ti(Ep(e),na)}(n);if(!fn(n)&&!0===n.multi){let i=this.records.get(t);i||(i=ti(void 0,na,!0),i.factory=()=>du(i.multi),this.records.set(t,i)),t=n,i.multi.push(n)}this.records.set(t,o)}hydrate(n,t,o){const i=z(null);try{if(t.value===wp)throw au(gt(n));return t.value===na&&(t.value=wp,t.value=t.factory(void 0,o)),"object"==typeof t.value&&t.value&&function pT(e){return null!==e&&"object"==typeof e&&"function"==typeof e.ngOnDestroy}(t.value)&&this._ngOnDestroyHooks.add(t.value),t.value}finally{z(i)}}injectableDefInScope(n){if(!n.providedIn)return!1;const t=Q(n.providedIn);return"string"==typeof t?"any"===t||this.scopes.has(t):this.injectorDefTypes.has(t)}removeOnDestroy(n){const t=this._onDestroyHooks.indexOf(n);-1!==t&&this._onDestroyHooks.splice(t,1)}}function bu(e){const n=qs(e),t=null!==n?n.factory:po(e);if(null!==t)return t;if(e instanceof R)throw new T(204,!1);if(e instanceof Function)return function fT(e){if(e.length>0)throw new T(204,!1);const t=function GI(e){return(e?.[Ys]??null)||null}(e);return null!==t?()=>t.factory(e):()=>new e}(e);throw new T(204,!1)}function Ep(e,n,t){let o;if(fn(e)){const i=Q(e);return po(i)||bu(i)}if(_u(e))o=()=>Q(e.useValue);else if(function bp(e){return!(!e||!e.useFactory)}(e))o=()=>e.useFactory(...du(e.deps||[]));else if(function Cp(e){return!(!e||!e.useExisting)}(e))o=(i,r)=>oe(Q(e.useExisting),void 0!==r&&8&r?8:void 0);else{const i=Q(e&&(e.useClass||e.provide));if(!function gT(e){return!!e.deps}(e))return po(i)||bu(i);o=()=>new i(...du(e.deps))}return o}function yr(e){if(e.destroyed)throw new T(205,!1)}function ti(e,n,t=!1){return{factory:e,value:n,multi:t?[]:void 0}}function Du(e,n){for(const t of e)Array.isArray(t)?Du(t,n):t&&iu(t)?Du(t.\u0275providers,n):n(t)}function Mp(e,n){let t;e instanceof _o?(yr(e),t=e):t=new oT(e);const i=un(t),r=pt(void 0);try{return n()}finally{un(i),pt(r)}}function wu(){return void 0!==dp()||null!=sr()}const Y=11,H=27;function Me(e){return Array.isArray(e)&&"object"==typeof e[1]}function st(e){return Array.isArray(e)&&!0===e[1]}function Tp(e){return!!(4&e.flags)}function pn(e){return e.componentOffset>-1}function si(e){return!(1&~e.flags)}function At(e){return!!e.template}function Hn(e){return!!(512&e[2])}function mn(e){return!(256&~e[2])}function $e(e){for(;Array.isArray(e);)e=e[0];return e}function ai(e,n){return $e(n[e])}function Le(e,n){return $e(n[e.index])}function li(e,n){return e.data[n]}function at(e,n){const t=n[e];return Me(t)?t:t[0]}function Tu(e){return!(128&~e[2])}function tt(e,n){return null==n?null:e[n]}function Rp(e){e[17]=0}function kp(e){1024&e[2]||(e[2]|=1024,Tu(e)&&ci(e))}function sa(e){return!!(9216&e[2]||e[24]?.dirty)}function Su(e){e[10].changeDetectionScheduler?.notify(8),64&e[2]&&(e[2]|=1024),sa(e)&&ci(e)}function ci(e){e[10].changeDetectionScheduler?.notify(0);let n=_n(e);for(;null!==n&&!(8192&n[2])&&(n[2]|=8192,Tu(n));)n=_n(n)}function aa(e,n){if(mn(e))throw new T(911,!1);null===e[21]&&(e[21]=[]),e[21].push(n)}function _n(e){const n=e[3];return st(n)?n[3]:n}function Lp(e){return e[7]??=[]}function Pp(e){return e.cleanup??=[]}const G={lFrame:Jp(null),bindingsEnabled:!0,skipHydrationRootTNode:null};let Ou=!1;function xu(){return G.bindingsEnabled}function w(){return G.lFrame.lView}function K(){return G.lFrame.tView}function B(e){return G.lFrame.contextLView=e,e[8]}function j(e){return G.lFrame.contextLView=null,e}function q(){let e=Up();for(;null!==e&&64===e.type;)e=e.parent;return e}function Up(){return G.lFrame.currentTNode}function vn(e,n){const t=G.lFrame;t.currentTNode=e,t.isParent=n}function $p(){return G.lFrame.isParent}function qp(){return Ou}function la(e){const n=Ou;return Ou=e,n}function lt(){const e=G.lFrame;let n=e.bindingRootIndex;return-1===n&&(n=e.bindingRootIndex=e.tView.bindingStartIndex),n}function mt(){return G.lFrame.bindingIndex++}function Cn(e){const n=G.lFrame,t=n.bindingIndex;return n.bindingIndex=n.bindingIndex+e,t}function AT(e,n){const t=G.lFrame;t.bindingIndex=t.bindingRootIndex=e,Ru(n)}function Ru(e){G.lFrame.currentDirectiveIndex=e}function Fu(){return G.lFrame.currentQueryIndex}function ca(e){G.lFrame.currentQueryIndex=e}function OT(e){const n=e[1];return 2===n.type?n.declTNode:1===n.type?e[5]:null}function Qp(e,n,t){if(4&t){let i=n,r=e;for(;!(i=i.parent,null!==i||1&t||(i=OT(r),null===i||(r=r[14],10&i.type))););if(null===i)return!1;n=i,e=r}const o=G.lFrame=Kp();return o.currentTNode=n,o.lView=e,!0}function Lu(e){const n=Kp(),t=e[1];G.lFrame=n,n.currentTNode=t.firstChild,n.lView=e,n.tView=t,n.contextLView=e,n.bindingIndex=t.bindingStartIndex,n.inI18n=!1}function Kp(){const e=G.lFrame,n=null===e?null:e.child;return null===n?Jp(e):n}function Jp(e){const n={currentTNode:null,isParent:!0,lView:null,tView:null,selectedIndex:-1,contextLView:null,elementDepthCount:0,currentNamespace:null,currentDirectiveIndex:-1,bindingRootIndex:-1,bindingIndex:-1,currentQueryIndex:0,parent:e,child:null,inI18n:!1};return null!==e&&(e.child=n),n}function Xp(){const e=G.lFrame;return G.lFrame=e.parent,e.currentTNode=null,e.lView=null,e}const em=Xp;function Pu(){const e=Xp();e.isParent=!0,e.tView=null,e.selectedIndex=-1,e.contextLView=null,e.elementDepthCount=0,e.currentDirectiveIndex=-1,e.currentNamespace=null,e.bindingRootIndex=-1,e.bindingIndex=-1,e.currentQueryIndex=0}function qe(){return G.lFrame.selectedIndex}function Do(e){G.lFrame.selectedIndex=e}function on(){const e=G.lFrame;return li(e.tView,e.selectedIndex)}let nm=!0;function ua(){return nm}function wr(e){nm=e}function om(e,n=null,t=null,o){const i=im(e,n,t,o);return i.resolveInjectorInitializers(),i}function im(e,n=null,t=null,o,i=new Set){const r=[t||_e,uT(e)];return o=o||("object"==typeof e?void 0:gt(e)),new _o(r,n||Cu(),o||null,i)}class Vt{static THROW_IF_NOT_FOUND=go;static NULL=new ea;static create(n,t){if(Array.isArray(n))return om({name:""},t,n,"");{const o=n.name??"";return om({name:o},n.parent,n.providers,o)}}static \u0275prov=X({token:Vt,providedIn:"any",factory:()=>oe(_p)});static __NG_ELEMENT_ID__=-1}const Bn=new R("");let bn=(()=>class e{static __NG_ELEMENT_ID__=PT;static __NG_ENV_ID__=t=>t})();class rm extends bn{_lView;constructor(n){super(),this._lView=n}get destroyed(){return mn(this._lView)}onDestroy(n){const t=this._lView;return aa(t,n),()=>function Au(e,n){if(null===e[21])return;const t=e[21].indexOf(n);-1!==t&&e[21].splice(t,1)}(t,n)}}function PT(){return new rm(w())}class di{_console=console;handleError(n){this._console.error("ERROR",n)}}const Dn=new R("",{providedIn:"root",factory:()=>{const e=F(Lt);let n;return t=>{e.destroyed&&!n?setTimeout(()=>{throw t}):(n??=e.get(di),n.handleError(t))}}}),VT={provide:mo,useValue:()=>{F(di)},multi:!0};function wo(e,n){const[t,o,i]=bI(e,n?.equal),r=t;return r.set=o,r.update=i,r.asReadonly=Vu.bind(r),r}function Vu(){const e=this[We];if(void 0===e.readonlyFn){const n=()=>this();n[We]=e,e.readonlyFn=n}return e.readonlyFn}function am(e){return function sm(e){return"function"==typeof e&&void 0!==e[We]}(e)&&"function"==typeof e.set}let Hu=(()=>class e{view;node;constructor(t,o){this.view=t,this.node=o}static __NG_ELEMENT_ID__=BT})();function BT(){return new Hu(w(),q())}class fi{}const lm=new R("",{providedIn:"root",factory:()=>!1}),cm=new R(""),um=new R("");let Eo=(()=>{class e{taskId=0;pendingTasks=new Set;destroyed=!1;pendingTask=new FI(!1);get hasPendingTasks(){return!this.destroyed&&this.pendingTask.value}get hasPendingTasksObservable(){return this.destroyed?new ht(t=>{t.next(!1),t.complete()}):this.pendingTask}add(){!this.hasPendingTasks&&!this.destroyed&&this.pendingTask.next(!0);const t=this.taskId++;return this.pendingTasks.add(t),t}has(t){return this.pendingTasks.has(t)}remove(t){this.pendingTasks.delete(t),0===this.pendingTasks.size&&this.hasPendingTasks&&this.pendingTask.next(!1)}ngOnDestroy(){this.pendingTasks.clear(),this.hasPendingTasks&&this.pendingTask.next(!1),this.destroyed=!0,this.pendingTask.unsubscribe()}static \u0275prov=X({token:e,providedIn:"root",factory:()=>new e})}return e})();function Er(...e){}let fm=(()=>{class e{static \u0275prov=X({token:e,providedIn:"root",factory:()=>new jT})}return e})();class jT{dirtyEffectCount=0;queues=new Map;add(n){this.enqueue(n),this.schedule(n)}schedule(n){n.dirty&&this.dirtyEffectCount++}remove(n){const o=this.queues.get(n.zone);o.has(n)&&(o.delete(n),n.dirty&&this.dirtyEffectCount--)}enqueue(n){const t=n.zone;this.queues.has(t)||this.queues.set(t,new Set);const o=this.queues.get(t);o.has(n)||o.add(n)}flush(){for(;this.dirtyEffectCount>0;){let n=!1;for(const[t,o]of this.queues)n||=null===t?this.flushQueue(o):t.run(()=>this.flushQueue(o));n||(this.dirtyEffectCount=0)}}flushQueue(n){let t=!1;for(const o of n)o.dirty&&(this.dirtyEffectCount--,t=!0,o.run());return t}}let hm=null;function Mr(){return hm}class $T{}function Mo(e){return n=>{if(function E0(e){return ke(e?.lift)}(n))return n.lift(function(t){try{return e(t,this)}catch(o){this.error(o)}});throw new TypeError("Unable to lift unknown Observable type")}}function jn(e,n,t,o,i){return new M0(e,n,t,o,i)}class M0 extends Zc{constructor(n,t,o,i,r,s){super(n),this.onFinalize=r,this.shouldUnsubscribe=s,this._next=t?function(a){try{t(a)}catch(l){n.error(l)}}:super._next,this._error=i?function(a){try{i(a)}catch(l){n.error(l)}finally{this.unsubscribe()}}:super._error,this._complete=o?function(){try{o()}catch(a){n.error(a)}finally{this.unsubscribe()}}:super._complete}unsubscribe(){var n;if(!this.shouldUnsubscribe||this.shouldUnsubscribe()){const{closed:t}=this;super.unsubscribe(),!t&&(null===(n=this.onFinalize)||void 0===n||n.call(this))}}}function $u(e,n){return Mo((t,o)=>{let i=0;t.subscribe(jn(o,r=>{o.next(e.call(n,r,i++))}))})}function wn(e){return{toString:e}.toString()}class V0{previousValue;currentValue;firstChange;constructor(n,t,o){this.previousValue=n,this.currentValue=t,this.firstChange=o}isFirstChange(){return this.firstChange}}function Sm(e,n,t,o){null!==n?n.applyValueToInputSignal(n,o):e[t]=o}const En=(()=>{const e=()=>Am;return e.ngInherit=!0,e})();function Am(e){return e.type.prototype.ngOnChanges&&(e.setInput=B0),H0}function H0(){const e=Om(this),n=e?.current;if(n){const t=e.previous;if(t===tn)e.previous=n;else for(let o in n)t[o]=n[o];e.current=null,this.ngOnChanges(n)}}function B0(e,n,t,o,i){const r=this.declaredInputs[o],s=Om(e)||function j0(e,n){return e[Nm]=n}(e,{previous:tn,current:null}),a=s.current||(s.current={}),l=s.previous,c=l[r];a[r]=new V0(c&&c.currentValue,t,l===tn),Sm(e,n,i,t)}const Nm="__ngSimpleChanges__";function Om(e){return e[Nm]||null}const Io=[],fe=function(e,n=null,t){for(let o=0;o=o)break}else n[l]<0&&(e[17]+=65536),(a>14>16&&(3&e[2])===n&&(e[2]+=16384,km(a,r)):km(a,r)}class Or{factory;name;injectImpl;resolving=!1;canSeeViewProviders;multi;componentProviders;index;providerFactory;constructor(n,t,o,i){this.factory=n,this.name=i,this.canSeeViewProviders=t,this.injectImpl=o}}function Lm(e){return 3===e||4===e||6===e}function Pm(e){return 64===e.charCodeAt(0)}function vi(e,n){if(null!==n&&0!==n.length)if(null===e||0===e.length)e=n.slice();else{let t=-1;for(let o=0;on){s=r-1;break}}}for(;r>16}(e),o=n;for(;t>0;)o=o[14],t--;return o}let Yu=!0;function _a(e){const n=Yu;return Yu=e,n}let K0=0;const rn={};function va(e,n){const t=jm(e,n);if(-1!==t)return t;const o=n[1];o.firstCreatePass&&(e.injectorIndex=n.length,Qu(o.data,e),Qu(n,null),Qu(o.blueprint,null));const i=ya(e,n),r=e.injectorIndex;if(Zu(i)){const s=xr(i),a=Rr(i,n),l=a[1].data;for(let c=0;c<8;c++)n[r+c]=a[s+c]|l[s+c]}return n[r+8]=i,r}function Qu(e,n){e.push(0,0,0,0,0,0,0,0,n)}function jm(e,n){return-1===e.injectorIndex||e.parent&&e.parent.injectorIndex===e.injectorIndex||null===n[e.injectorIndex+8]?-1:e.injectorIndex}function ya(e,n){if(e.parent&&-1!==e.parent.injectorIndex)return e.parent.injectorIndex;let t=0,o=null,i=n;for(;null!==i;){if(o=Zm(i),null===o)return-1;if(t++,i=i[14],-1!==o.injectorIndex)return o.injectorIndex|t<<16}return-1}function Ku(e,n,t){!function J0(e,n,t){let o;"string"==typeof t?o=t.charCodeAt(0)||0:t.hasOwnProperty(hr)&&(o=t[hr]),null==o&&(o=t[hr]=K0++);const i=255&o;n.data[e+(i>>5)]|=1<=0?255&n:nS:n}(t);if("function"==typeof r){if(!Qp(n,e,o))return 1&o?Um(i,0,o):$m(n,t,o,i);try{let s;if(s=r(o),null!=s||8&o)return s;lu()}finally{em()}}else if("number"==typeof r){let s=null,a=jm(e,n),l=-1,c=1&o?n[15][5]:null;for((-1===a||4&o)&&(l=-1===a?ya(e,n):n[a+8],-1!==l&&qm(o,!1)?(s=n[1],a=xr(l),n=Rr(l,n)):a=-1);-1!==a;){const u=n[1];if(Wm(r,a,u.data)){const d=eS(a,n,t,s,o,c);if(d!==rn)return d}l=n[a+8],-1!==l&&qm(o,n[1].data[a+8]===c)&&Wm(r,a,n)?(s=u,a=xr(l),n=Rr(l,n)):a=-1}}return i}function eS(e,n,t,o,i,r){const s=n[1],a=s.data[e+8],u=Ca(a,s,t,null==o?pn(a)&&Yu:o!=s&&!!(3&a.type),1&i&&r===a);return null!==u?kr(n,s,u,a,i):rn}function Ca(e,n,t,o,i){const r=e.providerIndexes,s=n.data,a=1048575&r,l=e.directiveStart,u=r>>20,g=i?a+u:e.directiveEnd;for(let h=o?a:a+u;h=l&&p.type===t)return h}if(i){const h=s[l];if(h&&At(h)&&h.type===t)return l}return null}function kr(e,n,t,o,i){let r=e[t];const s=n.data;if(r instanceof Or){const a=r;if(a.resolving)throw function ne(e){return"function"==typeof e?e.name||e.toString():"object"==typeof e&&null!=e&&"function"==typeof e.type?e.type.name||e.type.toString():Z(e)}(s[t]),au();const l=_a(a.canSeeViewProviders);a.resolving=!0;const d=a.injectImpl?pt(a.injectImpl):null;Qp(e,o,0);try{r=e[t]=a.factory(void 0,i,s,e,o),n.firstCreatePass&&t>=o.directiveStart&&function G0(e,n,t){const{ngOnChanges:o,ngOnInit:i,ngDoCheck:r}=n.type.prototype;if(o){const s=Am(n);(t.preOrderHooks??=[]).push(e,s),(t.preOrderCheckHooks??=[]).push(e,s)}i&&(t.preOrderHooks??=[]).push(0-e,i),r&&((t.preOrderHooks??=[]).push(e,r),(t.preOrderCheckHooks??=[]).push(e,r))}(t,s[t],n)}finally{null!==d&&pt(d),_a(l),a.resolving=!1,em()}}return r}function Wm(e,n,t){return!!(t[n+(e>>5)]&1<{const n=e.prototype.constructor,t=n[ho]||Ju(n),o=Object.prototype;let i=Object.getPrototypeOf(e.prototype).constructor;for(;i&&i!==o;){const r=i[ho]||Ju(i);if(r&&r!==t)return r;i=Object.getPrototypeOf(i)}return r=>new r})}function Ju(e){return Ws(e)?()=>{const n=Ju(Q(e));return n&&n()}:po(e)}function Zm(e){const n=e[1],t=n.type;return 2===t?n.declTNode:1===t?e[5]:null}function dS(){return yi(q(),w())}function yi(e,n){return new Nt(Le(e,n))}let Nt=(()=>class e{nativeElement;constructor(t){this.nativeElement=t}static __NG_ELEMENT_ID__=dS})();function Xm(e){return e instanceof Nt?e.nativeElement:e}function fS(){return this._results[Symbol.iterator]()}class hS{_emitDistinctChangesOnly;dirty=!0;_onDirty=void 0;_results=[];_changesDetected=!1;_changes=void 0;length=0;first=void 0;last=void 0;get changes(){return this._changes??=new Xt}constructor(n=!1){this._emitDistinctChangesOnly=n}get(n){return this._results[n]}map(n){return this._results.map(n)}filter(n){return this._results.filter(n)}find(n){return this._results.find(n)}reduce(n,t){return this._results.reduce(n,t)}forEach(n){this._results.forEach(n)}some(n){return this._results.some(n)}toArray(){return this._results.slice()}toString(){return this._results.toString()}reset(n,t){this.dirty=!1;const o=function Ft(e){return e.flat(Number.POSITIVE_INFINITY)}(n);(this._changesDetected=!function aT(e,n,t){if(e.length!==n.length)return!1;for(let o=0;oBS}),BS="ng",y_=new R(""),C_=new R("",{providedIn:"platform",factory:()=>"unknown"}),b_=new R("",{providedIn:"root",factory:()=>$n().body?.querySelector("[ngCspNonce]")?.getAttribute("ngCspNonce")||null}),ZS=new R("",{providedIn:"root",factory:()=>!1});function Sa(e){return!(32&~e.flags)}function W_(e,n){const t=e.contentQueries;if(null!==t){const o=z(null);try{for(let i=0;ie,createScript:e=>e,createScriptURL:e=>e})}catch{}return Fa}()?.createHTML(e)||e}function K_(e){return function Td(){if(void 0===La&&(La=null,Ie.trustedTypes))try{La=Ie.trustedTypes.createPolicy("angular#unsafe-bypass",{createHTML:e=>e,createScript:e=>e,createScriptURL:e=>e})}catch{}return La}()?.createHTML(e)||e}class ev{changingThisBreaksApplicationSecurity;constructor(n){this.changingThisBreaksApplicationSecurity=n}toString(){return`SafeValue must use [property]=binding: ${this.changingThisBreaksApplicationSecurity} (see ${sp})`}}function Gn(e){return e instanceof ev?e.changingThisBreaksApplicationSecurity:e}function Br(e,n){const t=function O1(e){return e instanceof ev&&e.getTypeName()||null}(e);if(null!=t&&t!==n){if("ResourceURL"===t&&"URL"===n)return!0;throw new Error(`Required a safe ${n}, got a ${t} (see ${sp})`)}return t===n}class x1{inertDocumentHelper;constructor(n){this.inertDocumentHelper=n}getInertBodyElement(n){n=""+n;try{const t=(new window.DOMParser).parseFromString(Ei(n),"text/html").body;return null===t?this.inertDocumentHelper.getInertBodyElement(n):(t.firstChild?.remove(),t)}catch{return null}}}class R1{defaultDoc;inertDocument;constructor(n){this.defaultDoc=n,this.inertDocument=this.defaultDoc.implementation.createHTMLDocument("sanitization-inert")}getInertBodyElement(n){const t=this.inertDocument.createElement("template");return t.innerHTML=Ei(n),t}}const F1=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:\/?#]*(?:[\/?#]|$))/i;function Sd(e){return(e=String(e)).match(F1)?e:"unsafe:"+e}function In(e){const n={};for(const t of e.split(","))n[t]=!0;return n}function jr(...e){const n={};for(const t of e)for(const o in t)t.hasOwnProperty(o)&&(n[o]=!0);return n}const nv=In("area,br,col,hr,img,wbr"),ov=In("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),iv=In("rp,rt"),Ad=jr(nv,jr(ov,In("address,article,aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul")),jr(iv,In("a,abbr,acronym,audio,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video")),jr(iv,ov)),Nd=In("background,cite,href,itemtype,longdesc,poster,src,xlink:href"),rv=jr(Nd,In("abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,scope,scrolling,shape,size,sizes,span,srclang,srcset,start,summary,tabindex,target,title,translate,type,usemap,valign,value,vspace,width"),In("aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext")),L1=In("script,style,template");class P1{sanitizedSomething=!1;buf=[];sanitizeChildren(n){let t=n.firstChild,o=!0,i=[];for(;t;)if(t.nodeType===Node.ELEMENT_NODE?o=this.startElement(t):t.nodeType===Node.TEXT_NODE?this.chars(t.nodeValue):this.sanitizedSomething=!0,o&&t.firstChild)i.push(t),t=B1(t);else for(;t;){t.nodeType===Node.ELEMENT_NODE&&this.endElement(t);let r=H1(t);if(r){t=r;break}t=i.pop()}return this.buf.join("")}startElement(n){const t=sv(n).toLowerCase();if(!Ad.hasOwnProperty(t))return this.sanitizedSomething=!0,!L1.hasOwnProperty(t);this.buf.push("<"),this.buf.push(t);const o=n.attributes;for(let i=0;i"),!0}endElement(n){const t=sv(n).toLowerCase();Ad.hasOwnProperty(t)&&!nv.hasOwnProperty(t)&&(this.buf.push(""))}chars(n){this.buf.push(lv(n))}}function H1(e){const n=e.nextSibling;if(n&&e!==n.previousSibling)throw av(n);return n}function B1(e){const n=e.firstChild;if(n&&function V1(e,n){return(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_CONTAINED_BY)!==Node.DOCUMENT_POSITION_CONTAINED_BY}(e,n))throw av(n);return n}function sv(e){const n=e.nodeName;return"string"==typeof n?n:"FORM"}function av(e){return new Error(`Failed to sanitize html because the element is clobbered: ${e.outerHTML}`)}const j1=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,U1=/([^\#-~ |!])/g;function lv(e){return e.replace(/&/g,"&").replace(j1,function(n){return"&#"+(1024*(n.charCodeAt(0)-55296)+(n.charCodeAt(1)-56320)+65536)+";"}).replace(U1,function(n){return"&#"+n.charCodeAt(0)+";"}).replace(//g,">")}let Pa;function Od(e){return"content"in e&&function z1(e){return e.nodeType===Node.ELEMENT_NODE&&"TEMPLATE"===e.nodeName}(e)?e.content:null}function Va(e,n,t){return e.createElement(n,t)}function So(e,n,t,o,i){e.insertBefore(n,t,o,i)}function dv(e,n,t){e.appendChild(n,t)}function fv(e,n,t,o,i){null!==o?So(e,n,t,o,i):dv(e,n,t)}function Ur(e,n,t,o){e.removeChild(null,n,t,o)}function gv(e,n,t){const{mergedAttrs:o,classes:i,styles:r}=t;null!==o&&function Y0(e,n,t){let o=0;for(;o-1){let r;for(;++ir?"":i[u+1].toLowerCase(),2&o&&c!==d){if(Wt(o))return!1;s=!0}}}}else{if(!s&&!Wt(o)&&!Wt(l))return!1;if(s&&Wt(l))continue;s=!1,o=l|1&o}}return Wt(o)||s}function Wt(e){return!(1&e)}function mA(e,n,t,o){if(null===n)return-1;let i=0;if(o||!t){let r=!1;for(;i-1)for(t++;t0?'="'+a+'"':"")+"]"}else 8&o?i+="."+s:4&o&&(i+=" "+s);else""!==i&&!Wt(s)&&(n+=wv(r,i),i=""),o=s,r=r||!Wt(o);t++}return""!==i&&(n+=wv(r,i)),n}const ce={};function Fd(e,n,t,o,i,r,s,a,l,c,u){const d=H+o,g=d+i,h=function EA(e,n){const t=[];for(let o=0;onull),s=o;if(n&&"object"==typeof n){const l=n;i=l.next?.bind(l),r=l.error?.bind(l),s=l.complete?.bind(l)}this.__isAsync&&(r=this.wrapInTimeout(r),i&&(i=this.wrapInTimeout(i)),s&&(s=this.wrapInTimeout(s)));const a=super.subscribe({next:i,error:r,complete:s});return n instanceof It&&n.add(a),a}wrapInTimeout(n){return t=>{const o=this.pendingTasks?.add();setTimeout(()=>{try{n(t)}finally{void 0!==o&&this.pendingTasks?.remove(o)}})}}};function Ov(e){let n,t;function o(){e=Er;try{void 0!==t&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(t),void 0!==n&&clearTimeout(n)}catch{}}return n=setTimeout(()=>{e(),o()}),"function"==typeof requestAnimationFrame&&(t=requestAnimationFrame(()=>{e(),o()})),()=>o()}function xv(e){return queueMicrotask(()=>e()),()=>{e=Er}}const jd="isAngularZone",za=jd+"_ID";let xA=0;class re{hasPendingMacrotasks=!1;hasPendingMicrotasks=!1;isStable=!0;onUnstable=new ve(!1);onMicrotaskEmpty=new ve(!1);onStable=new ve(!1);onError=new ve(!1);constructor(n){const{enableLongStackTrace:t=!1,shouldCoalesceEventChangeDetection:o=!1,shouldCoalesceRunChangeDetection:i=!1,scheduleInRootZone:r=Nv}=n;if(typeof Zone>"u")throw new T(908,!1);Zone.assertZonePatched();const s=this;s._nesting=0,s._outer=s._inner=Zone.current,Zone.TaskTrackingZoneSpec&&(s._inner=s._inner.fork(new Zone.TaskTrackingZoneSpec)),t&&Zone.longStackTraceZoneSpec&&(s._inner=s._inner.fork(Zone.longStackTraceZoneSpec)),s.shouldCoalesceEventChangeDetection=!i&&o,s.shouldCoalesceRunChangeDetection=i,s.callbackScheduled=!1,s.scheduleInRootZone=r,function FA(e){const n=()=>{!function kA(e){function n(){Ov(()=>{e.callbackScheduled=!1,$d(e),e.isCheckStableRunning=!0,Ud(e),e.isCheckStableRunning=!1})}e.isCheckStableRunning||e.callbackScheduled||(e.callbackScheduled=!0,e.scheduleInRootZone?Zone.root.run(()=>{n()}):e._outer.run(()=>{n()}),$d(e))}(e)},t=xA++;e._inner=e._inner.fork({name:"angular",properties:{[jd]:!0,[za]:t,[za+t]:!0},onInvokeTask:(o,i,r,s,a,l)=>{if(function LA(e){return Fv(e,"__ignore_ng_zone__")}(l))return o.invokeTask(r,s,a,l);try{return Rv(e),o.invokeTask(r,s,a,l)}finally{(e.shouldCoalesceEventChangeDetection&&"eventTask"===s.type||e.shouldCoalesceRunChangeDetection)&&n(),kv(e)}},onInvoke:(o,i,r,s,a,l,c)=>{try{return Rv(e),o.invoke(r,s,a,l,c)}finally{e.shouldCoalesceRunChangeDetection&&!e.callbackScheduled&&!function PA(e){return Fv(e,"__scheduler_tick__")}(l)&&n(),kv(e)}},onHasTask:(o,i,r,s)=>{o.hasTask(r,s),i===r&&("microTask"==s.change?(e._hasPendingMicrotasks=s.microTask,$d(e),Ud(e)):"macroTask"==s.change&&(e.hasPendingMacrotasks=s.macroTask))},onHandleError:(o,i,r,s)=>(o.handleError(r,s),e.runOutsideAngular(()=>e.onError.emit(s)),!1)})}(s)}static isInAngularZone(){return typeof Zone<"u"&&!0===Zone.current.get(jd)}static assertInAngularZone(){if(!re.isInAngularZone())throw new T(909,!1)}static assertNotInAngularZone(){if(re.isInAngularZone())throw new T(909,!1)}run(n,t,o){return this._inner.run(n,t,o)}runTask(n,t,o,i){const r=this._inner,s=r.scheduleEventTask("NgZoneEvent: "+i,n,RA,Er,Er);try{return r.runTask(s,t,o)}finally{r.cancelTask(s)}}runGuarded(n,t,o){return this._inner.runGuarded(n,t,o)}runOutsideAngular(n){return this._outer.run(n)}}const RA={};function Ud(e){if(0==e._nesting&&!e.hasPendingMicrotasks&&!e.isStable)try{e._nesting++,e.onMicrotaskEmpty.emit(null)}finally{if(e._nesting--,!e.hasPendingMicrotasks)try{e.runOutsideAngular(()=>e.onStable.emit(null))}finally{e.isStable=!0}}}function $d(e){e.hasPendingMicrotasks=!!(e._hasPendingMicrotasks||(e.shouldCoalesceEventChangeDetection||e.shouldCoalesceRunChangeDetection)&&!0===e.callbackScheduled)}function Rv(e){e._nesting++,e.isStable&&(e.isStable=!1,e.onUnstable.emit(null))}function kv(e){e._nesting--,Ud(e)}class zd{hasPendingMicrotasks=!1;hasPendingMacrotasks=!1;isStable=!0;onUnstable=new ve;onMicrotaskEmpty=new ve;onStable=new ve;onError=new ve;run(n,t,o){return n.apply(t,o)}runGuarded(n,t,o){return n.apply(t,o)}runOutsideAngular(n){return n()}runTask(n,t,o,i){return n.apply(t,o)}}function Fv(e,n){return!(!Array.isArray(e)||1!==e.length)&&!0===e[0]?.data?.[n]}let Lv=(()=>{class e{impl=null;execute(){this.impl?.execute()}static \u0275prov=X({token:e,providedIn:"root",factory:()=>new e})}return e})();const Pv=[0,1,2,3];let HA=(()=>{class e{ngZone=F(re);scheduler=F(fi);errorHandler=F(di,{optional:!0});sequences=new Set;deferredRegistrations=new Set;executing=!1;constructor(){F(Wr,{optional:!0})}execute(){const t=this.sequences.size>0;t&&fe(16),this.executing=!0;for(const o of Pv)for(const i of this.sequences)if(!i.erroredOrDestroyed&&i.hooks[o])try{i.pipelinedValue=this.ngZone.runOutsideAngular(()=>this.maybeTrace(()=>(0,i.hooks[o])(i.pipelinedValue),i.snapshot))}catch(r){i.erroredOrDestroyed=!0,this.errorHandler?.handleError(r)}this.executing=!1;for(const o of this.sequences)o.afterRun(),o.once&&(this.sequences.delete(o),o.destroy());for(const o of this.deferredRegistrations)this.sequences.add(o);this.deferredRegistrations.size>0&&this.scheduler.notify(7),this.deferredRegistrations.clear(),t&&fe(17)}register(t){const{view:o}=t;void 0!==o?((o[25]??=[]).push(t),ci(o),o[2]|=8192):this.executing?this.deferredRegistrations.add(t):this.addSequence(t)}addSequence(t){this.sequences.add(t),this.scheduler.notify(7)}unregister(t){this.executing&&this.sequences.has(t)?(t.erroredOrDestroyed=!0,t.pipelinedValue=void 0,t.once=!0):(this.sequences.delete(t),this.deferredRegistrations.delete(t))}maybeTrace(t,o){return o?o.run(Bd.AFTER_NEXT_RENDER,t):t()}static \u0275prov=X({token:e,providedIn:"root",factory:()=>new e})}return e})();class Vv{impl;hooks;view;once;snapshot;erroredOrDestroyed=!1;pipelinedValue=void 0;unregisterOnDestroy;constructor(n,t,o,i,r,s=null){this.impl=n,this.hooks=t,this.view=o,this.once=i,this.snapshot=s,this.unregisterOnDestroy=r?.onDestroy(()=>this.destroy())}afterRun(){this.erroredOrDestroyed=!1,this.pipelinedValue=void 0,this.snapshot?.dispose(),this.snapshot=null}destroy(){this.impl.unregister(this),this.unregisterOnDestroy?.();const n=this.view?.[25];n&&(this.view[25]=n.filter(t=>t!==this))}}function Gd(e,n){const t=n?.injector??F(Vt);return nt("NgAfterNextRender"),function Hv(e,n,t,o){const i=n.get(Lv);i.impl??=n.get(HA);const r=n.get(Wr,null,{optional:!0}),s=!0!==t?.manualCleanup?n.get(bn):null,a=n.get(Hu,null,{optional:!0}),l=new Vv(i.impl,function jA(e){return e instanceof Function?[void 0,void 0,e,void 0]:[e.earlyRead,e.write,e.mixedReadWrite,e.read]}(e),a?.view,o,s,r?.snapshot(null));return i.impl.register(l),l}(e,t,n,!0)}const Ga=new R("",{providedIn:"root",factory:()=>({queue:new Set,isScheduled:!1,scheduler:null})});function Bv(e,n,t){const o=e.get(Ga);if(Array.isArray(n))for(const i of n)o.queue.add(i),t?.detachedLeaveAnimationFns?.push(i);else o.queue.add(n),t?.detachedLeaveAnimationFns?.push(n);o.scheduler&&o.scheduler(e)}function jv(e,n,t,o){const i=e?.[26]?.enter;null!==n&&i&&i.has(t.index)&&function Wd(e,n){for(const[t,o]of n)Bv(e,o.animateFns)}(o,i)}function Si(e,n,t,o,i,r,s,a){if(null!=i){let l,c=!1;st(i)?l=i:Me(i)&&(c=!0,i=i[0]);const u=$e(i);0===e&&null!==o?(jv(a,o,r,t),null==s?dv(n,o,u):So(n,o,u,s||null,!0)):1===e&&null!==o?(jv(a,o,r,t),So(n,o,u,s||null,!0)):2===e?zv(a,r,t,d=>{Ur(n,u,c,d)}):3===e&&zv(a,r,t,()=>{n.destroyNode(u)}),null!=l&&function QA(e,n,t,o,i,r,s){const a=o[7];a!==$e(o)&&Si(n,e,t,r,a,i,s);for(let c=10;c=0?o[a]():o[-a].unsubscribe(),s+=2}else t[s].call(o[t[s+1]]);null!==o&&(n[7]=null);const i=n[21];if(null!==i){n[21]=null;for(let s=0;s{if(i.leave&&i.leave.has(n.index)){const s=i.leave.get(n.index),a=[];if(s){for(let l=0;l{e[26].running=void 0,Ao.delete(e),n(!0)}):n(!1)}(e,o)}else e&&Ao.delete(e),o(!1)},i)}function Yd(e,n,t){return function Gv(e,n,t){let o=n;for(;null!==o&&168&o.type;)o=(n=o).parent;if(null===o)return t[0];if(pn(o)){const{encapsulation:i}=e.data[o.directiveStart+o.componentOffset];if(i===Mn.None||i===Mn.Emulated)return null}return Le(o,t)}(e,n.parent,t)}let Zv=function qv(e,n,t){return 40&e.type?Le(e,t):null};function Kd(e,n,t,o){const i=Yd(e,o,n),r=n[Y],a=function Wv(e,n,t){return Zv(e,n,t)}(o.parent||n[5],o,n);if(null!=i)if(Array.isArray(t))for(let l=0;lH&&Mv(e,n,H,!1),fe(s?2:0,i,t),t(o,i)}finally{Do(r),fe(s?3:1,i,t)}}function Ya(e,n,t){(function nN(e,n,t){const o=t.directiveStart,i=t.directiveEnd;pn(t)&&function MA(e,n,t){const o=Le(n,e),i=function Ev(e){const n=e.tView;return null===n||n.incompleteFirstPass?e.tView=Fd(1,null,e.template,e.decls,e.vars,e.directiveDefs,e.pipeDefs,e.viewQuery,e.schemas,e.consts,e.id):n}(t),r=e[10].rendererFactory,s=Pd(e,ja(e,i,null,Ld(t),o,n,null,r.createRenderer(o,t),null,null,null));e[n.index]=s}(n,t,e.data[o+t.componentOffset]),e.firstCreatePass||va(t,n);const r=t.initialInputs;for(let s=o;snull;function Xd(e,n,t,o,i,r){Xa(e,n[1],n,t,o)?pn(e)&&function ey(e,n){const t=at(n,e);16&t[2]||(t[2]|=64)}(n,e.index):(3&e.type&&(t=function tN(e){return"class"===e?"className":"for"===e?"htmlFor":"formaction"===e?"formAction":"innerHtml"===e?"innerHTML":"readonly"===e?"readOnly":"tabindex"===e?"tabIndex":e}(t)),function ef(e,n,t,o,i,r){if(3&e.type){const s=Le(e,n);o=null!=r?r(o,e.value||"",t):o,i.setProperty(s,t,o)}}(e,n,t,o,i,r))}function iN(e,n){null!==e.hostBindings&&e.hostBindings(1,n)}function tf(e,n){const t=e.directiveRegistry;let o=null;if(t)for(let i=0;i{ci(e.lView)},consumerOnSignalRead(){this.lView[24]=this}},_N={...Qo,consumerIsAlwaysLive:!0,kind:"template",consumerMarkedDirty:e=>{let n=_n(e.lView);for(;n&&!ry(n[1]);)n=_n(n);n&&kp(n)},consumerOnSignalRead(){this.lView[24]=this}};function ry(e){return 2!==e.type}function sy(e){if(null===e[23])return;let n=!0;for(;n;){let t=!1;for(const o of e[23])o.dirty&&(t=!0,null===o.zone||Zone.current===o.zone?o.run():o.zone.run(()=>o.run()));n=t&&!!(8192&e[2])}}function tl(e,n=0){const o=e[10].rendererFactory;o.begin?.();try{!function yN(e,n){const t=qp();try{la(!0),of(e,n);let o=0;for(;sa(e);){if(100===o)throw new T(103,!1);o++,of(e,1)}}finally{la(t)}}(e,n)}finally{o.end?.()}}function ay(e,n,t,o){if(mn(n))return;const i=n[2];Lu(n);let a=!0,l=null,c=null;ry(e)?(c=function fN(e){return e[24]??function hN(e){const n=iy.pop()??Object.create(pN);return n.lView=e,n}(e)}(n),l=Ko(c)):null===function jc(){return Je}()?(a=!1,c=function mN(e){const n=e[24]??Object.create(_N);return n.lView=e,n}(n),l=Ko(c)):n[24]&&(ur(n[24]),n[24]=null);try{Rp(n),function Zp(e){return G.lFrame.bindingIndex=e}(e.bindingStartIndex),null!==t&&Jv(e,n,t,2,o);const u=!(3&~i);if(u){const h=e.preOrderCheckHooks;null!==h&&pa(n,h,null)}else{const h=e.preOrderHooks;null!==h&&ma(n,h,0,null),Wu(n,0)}if(function CN(e){for(let n=u_(e);null!==n;n=d_(n)){if(!(2&n[2]))continue;const t=n[9];for(let o=0;o0&&(t[i-1][4]=n),o0&&(e[t-1][4]=o[4]);const r=Ks(e,10+n);!function Uv(e,n){$v(e,n),n[0]=null,n[5]=null}(o[1],o);const s=r[18];null!==s&&s.detachView(r[1]),o[3]=null,o[4]=null,o[2]&=-129}return o}function hy(e,n){const t=e[9],o=n[3];(Me(o)||n[15]!==o[3][15])&&(e[2]|=2),null===t?e[9]=[n]:t.push(n)}class Qr{_lView;_cdRefInjectingView;_appRef=null;_attachedToViewContainer=!1;exhaustive;get rootNodes(){const n=this._lView,t=n[1];return Zr(t,n,t.firstChild,[])}constructor(n,t){this._lView=n,this._cdRefInjectingView=t}get context(){return this._lView[8]}set context(n){this._lView[8]=n}get destroyed(){return mn(this._lView)}destroy(){if(this._appRef)this._appRef.detachView(this);else if(this._attachedToViewContainer){const n=this._lView[3];if(st(n)){const t=n[8],o=t?t.indexOf(this):-1;o>-1&&(Yr(n,o),Ks(t,o))}this._attachedToViewContainer=!1}qr(this._lView[1],this._lView)}onDestroy(n){aa(this._lView,n)}markForCheck(){Oi(this._cdRefInjectingView||this._lView,4)}detach(){this._lView[2]&=-129}reattach(){Su(this._lView),this._lView[2]|=128}detectChanges(){this._lView[2]|=1024,tl(this._lView)}checkNoChanges(){}attachToViewContainerRef(){if(this._appRef)throw new T(902,!1);this._attachedToViewContainer=!0}detachFromAppRef(){this._appRef=null;const n=Hn(this._lView),t=this._lView[16];null!==t&&!n&&qd(t,this._lView),$v(this._lView[1],this._lView)}attachToAppRef(n){if(this._attachedToViewContainer)throw new T(902,!1);this._appRef=n;const t=Hn(this._lView),o=this._lView[16];null!==o&&!t&&hy(o,this._lView),Su(this._lView)}}let Tn=(()=>class e{_declarationLView;_declarationTContainer;elementRef;static __NG_ELEMENT_ID__=EN;constructor(t,o,i){this._declarationLView=t,this._declarationTContainer=o,this.elementRef=i}get ssrId(){return this._declarationTContainer.tView?.ssrId||null}createEmbeddedView(t,o){return this.createEmbeddedViewImpl(t,o)}createEmbeddedViewImpl(t,o,i){const r=Ni(this._declarationLView,this._declarationTContainer,t,{embeddedViewInjector:o,dehydratedView:i});return new Qr(r)}})();function EN(){return nl(q(),w())}function nl(e,n){return 4&e.type?new Tn(n,e,yi(e,n)):null}function xo(e,n,t,o,i){let r=e.data[n];if(null===r)r=function cf(e,n,t,o,i){const r=Up(),s=$p(),l=e.data[n]=function RN(e,n,t,o,i,r){let s=n?n.injectorIndex:-1,a=0;return function Hp(){return null!==G.skipHydrationRootTNode}()&&(a|=128),{type:t,index:o,insertBeforeIndex:null,injectorIndex:s,directiveStart:-1,directiveEnd:-1,directiveStylingLast:-1,componentOffset:-1,propertyBindings:null,flags:a,providerIndexes:0,value:i,attrs:r,mergedAttrs:null,localNames:null,initialInputs:null,inputs:null,hostDirectiveInputs:null,outputs:null,hostDirectiveOutputs:null,directiveToIndex:null,tView:null,next:null,prev:null,projectionNext:null,child:null,parent:n,projection:null,styles:null,stylesWithoutHost:null,residualStyles:void 0,classes:null,classesWithoutHost:null,residualClasses:void 0,classBindings:0,styleBindings:0}}(0,s?r:r&&r.parent,t,n,o,i);return function xN(e,n,t,o){null===e.firstChild&&(e.firstChild=n),null!==t&&(o?null==t.child&&null!==n.parent&&(t.child=n):null===t.next&&(t.next=n,n.prev=t))}(e,l,r,s),l}(e,n,t,o,i),function ST(){return G.lFrame.inI18n}()&&(r.flags|=32);else if(64&r.type){r.type=t,r.value=o,r.attrs=i;const s=function Dr(){const e=G.lFrame,n=e.currentTNode;return e.isParent?n:n.parent}();r.injectorIndex=null===s?-1:s.injectorIndex}return vn(r,!0),r}function xy(e,n){let t=0,o=e.firstChild;if(o){const i=e.data.r;for(;tclass e{destroyNode=null;static __NG_ELEMENT_ID__=()=>function _O(){const e=w(),t=at(q().index,e);return(Me(t)?t:e)[Y]}()})(),vO=(()=>{class e{static \u0275prov=X({token:e,providedIn:"root",factory:()=>null})}return e})();const vf={};class Fi{injector;parentInjector;constructor(n,t){this.injector=n,this.parentInjector=t}get(n,t,o){const i=this.injector.get(n,vf,o);return i!==vf||t===vf?i:this.parentInjector.get(n,t,o)}}function fl(e,n,t){let o=t?e.styles:null,i=t?e.classes:null,r=0;if(null!==n)for(let s=0;s0&&(t.directiveToIndex=new Map);for(let g=0;g0;){const t=e[--n];if("number"==typeof t&&t<0)return t}return 0})(s)!=a&&s.push(a),s.push(t,o,r)}}(e,n,o,zr(e,t,i.hostVars,ce),i)}function NO(e,n,t){if(t){if(n.exportAs)for(let o=0;o{const[t,o,i]=e[n],r={propName:t,templateName:n,isSignal:0!==(o&Ua.SignalBased)};return i&&(r.transform=i),r})}(this.componentDef.inputs),this.cachedInputs}get outputs(){return this.cachedOutputs??=function GO(e){return Object.keys(e).map(n=>({propName:e[n],templateName:n}))}(this.componentDef.outputs),this.cachedOutputs}constructor(n,t){super(),this.componentDef=n,this.ngModule=t,this.componentType=n.type,this.selector=function DA(e){return e.map(bA).join(",")}(n.selectors),this.ngContentSelectors=n.ngContentSelectors??[],this.isBoundToModule=!!t}create(n,t,o,i,r,s){fe(22);const a=z(null);try{const l=this.componentDef,c=function QO(e,n,t,o){const i=e?["ng-version","20.3.15"]:function wA(e){const n=[],t=[];let o=1,i=2;for(;o{if(1&t&&e)for(const o of e)o.create();if(2&t&&n)for(const o of n)o.update()}:null}(r,s),1,a,l,null,null,null,[i],null)}(o,l,s,r),u=function WO(e,n,t){let o=n instanceof Lt?n:n?.injector;return o&&null!==e.getStandaloneInjector&&(o=e.getStandaloneInjector(o)||o),o?new Fi(t,o):t}(l,i||this.ngModule,n),d=function qO(e){const n=e.get(mf,null);if(null===n)throw new T(407,!1);return{rendererFactory:n,sanitizer:e.get(vO,null),changeDetectionScheduler:e.get(fi,null),ngReflect:!1}}(u),g=d.rendererFactory.createRenderer(null,l),h=o?function JA(e,n,t,o){const r=o.get(ZS,!1)||t===Mn.ShadowDom,s=e.selectRootElement(n,r);return function XA(e){Xv(e)}(s),s}(g,o,l.encapsulation,u):function ZO(e,n){const t=function YO(e){return(e.selectors[0][0]||"div").toLowerCase()}(e);return Va(n,t,"svg"===t?"svg":"math"===t?"math":null)}(l,g),p=s?.some(Qy)||r?.some(N=>"function"!=typeof N&&N.bindings.some(Qy)),b=ja(null,c,null,512|Ld(l),null,null,d,g,u,null,null);b[H]=h,Lu(b);let I=null;try{const N=yf(H,b,2,"#host",()=>c.directiveRegistry,!0,0);gv(g,h,N),vt(h,b),Ya(c,b,N),Ed(c,N,b),Cf(c,N),void 0!==t&&function XO(e,n,t){const o=e.projection=[];for(let i=0;iclass e{static __NG_ELEMENT_ID__=ex})();function ex(){return Xy(q(),w())}const tx=ln,Ky=class extends tx{_lContainer;_hostTNode;_hostLView;constructor(n,t,o){super(),this._lContainer=n,this._hostTNode=t,this._hostLView=o}get element(){return yi(this._hostTNode,this._hostLView)}get injector(){return new Se(this._hostTNode,this._hostLView)}get parentInjector(){const n=ya(this._hostTNode,this._hostLView);if(Zu(n)){const t=Rr(n,this._hostLView),o=xr(n);return new Se(t[1].data[o+8],t)}return new Se(null,this._hostLView)}clear(){for(;this.length>0;)this.remove(this.length-1)}get(n){const t=Jy(this._lContainer);return null!==t&&t[n]||null}get length(){return this._lContainer.length-10}createEmbeddedView(n,t,o){let i,r;"number"==typeof o?i=o:null!=o&&(i=o.index,r=o.injector);const a=n.createEmbeddedViewImpl(t||{},r,null);return this.insertImpl(a,i,Oo(this._hostTNode,null)),a}createComponent(n,t,o,i,r,s,a){const l=n&&!function Nr(e){return"function"==typeof e}(n);let c;if(l)c=t;else{const I=t||{};c=I.index,o=I.injector,i=I.projectableNodes,r=I.environmentInjector||I.ngModuleRef,s=I.directives,a=I.bindings}const u=l?n:new Df(le(n)),d=o||this.parentInjector;if(!r&&null==u.ngModule){const N=(l?d:this.parentInjector).get(Lt,null);N&&(r=N)}le(u.componentType??{});const b=u.create(d,i,null,r,s,a);return this.insertImpl(b.hostView,c,Oo(this._hostTNode,null)),b}insert(n,t){return this.insertImpl(n,t,!0)}insertImpl(n,t,o){const i=n._lView;if(function DT(e){return st(e[3])}(i)){const a=this.indexOf(n);if(-1!==a)this.detach(a);else{const l=i[3],c=new Ky(l,l[5],l[3]);c.detach(c.indexOf(n))}}const r=this._adjustIndex(t),s=this._lContainer;return xi(s,i,r,o),n.attachToViewContainerRef(),gp(wf(s),r,n),n}move(n,t){return this.insert(n,t)}indexOf(n){const t=Jy(this._lContainer);return null!==t?t.indexOf(n):-1}remove(n){const t=this._adjustIndex(n,-1),o=Yr(this._lContainer,t);o&&(Ks(wf(this._lContainer),t),qr(o[1],o))}detach(n){const t=this._adjustIndex(n,-1),o=Yr(this._lContainer,t);return o&&null!=Ks(wf(this._lContainer),t)?new Qr(o):null}_adjustIndex(n,t=0){return n??this.length+t}};function Jy(e){return e[8]}function wf(e){return e[8]||(e[8]=[])}function Xy(e,n){let t;const o=n[e.index];return st(o)?t=o:(t=dy(o,n,null,e),n[e.index]=t,Pd(n,t)),eC(t,n,e,o),new Ky(t,e,n)}let eC=function nC(e,n,t,o){if(e[7])return;let i;i=8&t.type?$e(o):function nx(e,n){const t=e[Y],o=t.createComment(""),i=Le(n,e),r=t.parentNode(i);return So(t,r,o,t.nextSibling(i),!1),o}(n,t),e[7]=i};class Mf{queryList;matches=null;constructor(n){this.queryList=n}clone(){return new Mf(this.queryList)}setDirty(){this.queryList.setDirty()}}class If{queries;constructor(n=[]){this.queries=n}createEmbeddedView(n){const t=n.queries;if(null!==t){const o=null!==n.contentQueries?n.contentQueries[0]:t.length,i=[];for(let r=0;rn.trim())}(n):n}}class Tf{queries;constructor(n=[]){this.queries=n}elementStart(n,t){for(let o=0;o0)o.push(s[a/2]);else{const c=r[a+1],u=n[-l];for(let d=10;dt()),this.destroyCbs=null}onDestroy(n){this.destroyCbs.push(n)}}class gC extends yx{moduleType;constructor(n){super(),this.moduleType=n}create(n){return new kf(this.moduleType,n,[])}}class Dx extends Fo{injector;componentFactoryResolver=new Yy(this);instance=null;constructor(n){super();const t=new _o([...n.providers,{provide:Fo,useValue:this},{provide:ul,useValue:this.componentFactoryResolver}],n.parent||Cu(),n.debugName,new Set(["environment"]));this.injector=t,n.runEnvironmentInitializers&&t.resolveInjectorInitializers()}destroy(){this.injector.destroy()}onDestroy(n){this.injector.onDestroy(n)}}let wx=(()=>{class e{_injector;cachedInjectors=new Map;constructor(t){this._injector=t}getOrCreateStandaloneInjector(t){if(!t.standalone)return null;if(!this.cachedInjectors.has(t)){const o=pu(0,t.type),i=o.length>0?function pC(e,n,t=null){return new Dx({providers:e,parent:n,debugName:t,runEnvironmentInitializers:!0}).injector}([o],this._injector,`Standalone[${t.type.name}]`):null;this.cachedInjectors.set(t,i)}return this.cachedInjectors.get(t)}ngOnDestroy(){try{for(const t of this.cachedInjectors.values())null!==t&&t.destroy()}finally{this.cachedInjectors.clear()}}static \u0275prov=X({token:e,providedIn:"environment",factory:()=>new e(oe(Lt))})}return e})();function qt(e){return wn(()=>{const n=_C(e),t={...n,decls:e.decls,vars:e.vars,template:e.template,consts:e.consts||null,ngContentSelectors:e.ngContentSelectors,onPush:e.changeDetection===Da.OnPush,directiveDefs:null,pipeDefs:null,dependencies:n.standalone&&e.dependencies||null,getStandaloneInjector:n.standalone?i=>i.get(wx).getOrCreateStandaloneInjector(t):null,getExternalStyles:null,signals:e.signals??!1,data:e.data||{},encapsulation:e.encapsulation||Mn.Emulated,styles:e.styles||_e,_:null,schemas:e.schemas||null,tView:null,id:""};n.standalone&&nt("NgStandalone"),vC(t);const o=e.dependencies;return t.directiveDefs=ml(o,mC),t.pipeDefs=ml(o,Gt),t.id=function Tx(e){let n=0;const o=[e.selectors,e.ngContentSelectors,e.hostVars,e.hostAttrs,"function"==typeof e.consts?"":e.consts,e.vars,e.decls,e.encapsulation,e.standalone,e.signals,e.exportAs,JSON.stringify(e.inputs),JSON.stringify(e.outputs),Object.getOwnPropertyNames(e.type.prototype),!!e.contentQueries,!!e.viewQuery];for(const r of o.join("|"))n=Math.imul(31,n)+r.charCodeAt(0)|0;return n+=2147483648,"c"+n}(t),t})}function mC(e){return le(e)||rt(e)}function Qn(e){return wn(()=>({type:e.type,bootstrap:e.bootstrap||_e,declarations:e.declarations||_e,imports:e.imports||_e,exports:e.exports||_e,transitiveCompileScopes:null,schemas:e.schemas||null,id:e.id||null}))}function Ex(e,n){if(null==e)return tn;const t={};for(const o in e)if(e.hasOwnProperty(o)){const i=e[o];let r,s,a,l;Array.isArray(i)?(a=i[0],r=i[1],s=i[2]??r,l=i[3]||null):(r=i,s=i,a=Ua.None,l=null),t[r]=[o,a,l],n[r]=s}return t}function Mx(e){if(null==e)return tn;const n={};for(const t in e)e.hasOwnProperty(t)&&(n[e[t]]=t);return n}function W(e){return wn(()=>{const n=_C(e);return vC(n),n})}function yt(e){return{type:e.type,name:e.name,factory:null,pure:!1!==e.pure,standalone:e.standalone??!0,onDestroy:e.type.prototype.ngOnDestroy||null}}function _C(e){const n={};return{type:e.type,providersResolver:null,factory:null,hostBindings:e.hostBindings||null,hostVars:e.hostVars||0,hostAttrs:e.hostAttrs||null,contentQueries:e.contentQueries||null,declaredInputs:n,inputConfig:e.inputs||tn,exportAs:e.exportAs||null,standalone:e.standalone??!0,signals:!0===e.signals,selectors:e.selectors||_e,viewQuery:e.viewQuery||null,features:e.features||null,setInput:null,resolveHostDirectives:null,hostDirectives:null,inputs:Ex(e.inputs,n),outputs:Mx(e.outputs),debugInfo:null}}function vC(e){e.features?.forEach(n=>n(e))}function ml(e,n){return e?()=>{const t="function"==typeof e?e():e,o=[];for(const i of t){const r=n(i);null!==r&&o.push(r)}return o}:null}function ae(e){let n=function yC(e){return Object.getPrototypeOf(e.prototype).constructor}(e.type),t=!0;const o=[e];for(;n;){let i;if(At(e))i=n.\u0275cmp||n.\u0275dir;else{if(n.\u0275cmp)throw new T(903,!1);i=n.\u0275dir}if(i){if(t){o.push(i);const s=e;s.inputs=Ff(e.inputs),s.declaredInputs=Ff(e.declaredInputs),s.outputs=Ff(e.outputs);const a=i.hostBindings;a&&xx(e,a);const l=i.viewQuery,c=i.contentQueries;if(l&&Nx(e,l),c&&Ox(e,c),Sx(e,i),PI(e.outputs,i.outputs),At(i)&&i.data.animation){const u=e.data;u.animation=(u.animation||[]).concat(i.data.animation)}}const r=i.features;if(r)for(let s=0;s=0;o--){const i=e[o];i.hostVars=n+=i.hostVars,i.hostAttrs=vi(i.hostAttrs,t=vi(t,i.hostAttrs))}}(o)}function Sx(e,n){for(const t in n.inputs){if(!n.inputs.hasOwnProperty(t)||e.inputs.hasOwnProperty(t))continue;const o=n.inputs[t];void 0!==o&&(e.inputs[t]=o,e.declaredInputs[t]=n.declaredInputs[t])}}function Ff(e){return e===tn?{}:e===_e?[]:e}function Nx(e,n){const t=e.viewQuery;e.viewQuery=t?(o,i)=>{n(o,i),t(o,i)}:n}function Ox(e,n){const t=e.contentQueries;e.contentQueries=t?(o,i,r)=>{n(o,i,r),t(o,i,r)}:n}function xx(e,n){const t=e.hostBindings;e.hostBindings=t?(o,i)=>{n(o,i),t(o,i)}:n}function EC(e,n,t,o,i,r,s,a){if(t.firstCreatePass){e.mergedAttrs=vi(e.mergedAttrs,e.attrs);const u=e.tView=Fd(2,e,i,r,s,t.directiveRegistry,t.pipeRegistry,null,t.schemas,t.consts,null);null!==t.queries&&(t.queries.template(t,e),u.queries=t.queries.embeddedTView(e))}a&&(e.flags|=a),vn(e,!1);const l=IC(t,n,e,o);ua()&&Kd(t,n,l,e),vt(l,n);const c=dy(l,n,l,e);n[o+H]=c,Pd(n,c)}function Lo(e,n,t,o,i,r,s,a,l,c,u){const d=t+H;let g;if(n.firstCreatePass){if(g=xo(n,d,4,s||null,a||null),null!=c){const h=tt(n.consts,c);g.localNames=[];for(let p=0;p{class e{_ngZone;registry;_isZoneStable=!0;_callbacks=[];_taskTrackingZone=null;_destroyRef;constructor(t,o,i){this._ngZone=t,this.registry=o,wu()&&(this._destroyRef=F(bn,{optional:!0})??void 0),Gf||(function ZR(e){Gf=e}(i),i.addToWindow(o)),this._watchAngularEvents(),t.run(()=>{this._taskTrackingZone=typeof Zone>"u"?null:Zone.current.get("TaskTrackingZone")})}_watchAngularEvents(){const t=this._ngZone.onUnstable.subscribe({next:()=>{this._isZoneStable=!1}}),o=this._ngZone.runOutsideAngular(()=>this._ngZone.onStable.subscribe({next:()=>{re.assertNotInAngularZone(),queueMicrotask(()=>{this._isZoneStable=!0,this._runCallbacksIfReady()})}}));this._destroyRef?.onDestroy(()=>{t.unsubscribe(),o.unsubscribe()})}isStable(){return this._isZoneStable&&!this._ngZone.hasPendingMacrotasks}_runCallbacksIfReady(){if(this.isStable())queueMicrotask(()=>{for(;0!==this._callbacks.length;){let t=this._callbacks.pop();clearTimeout(t.timeoutId),t.doneCb()}});else{let t=this.getPendingTasks();this._callbacks=this._callbacks.filter(o=>!o.updateCb||!o.updateCb(t)||(clearTimeout(o.timeoutId),!1))}}getPendingTasks(){return this._taskTrackingZone?this._taskTrackingZone.macroTasks.map(t=>({source:t.source,creationLocation:t.creationLocation,data:t.data})):[]}addCallback(t,o,i){let r=-1;o&&o>0&&(r=setTimeout(()=>{this._callbacks=this._callbacks.filter(s=>s.timeoutId!==r),t()},o)),this._callbacks.push({doneCb:t,timeoutId:r,updateCb:i})}whenStable(t,o,i){if(i&&!this._taskTrackingZone)throw new Error('Task tracking zone is required when passing an update callback to whenStable(). Is "zone.js/plugins/task-tracking" loaded?');this.addCallback(t,o,i),this._runCallbacksIfReady()}registerApplication(t){this.registry.registerApplication(t,this)}unregisterApplication(t){this.registry.unregisterApplication(t)}findProviders(t,o,i){return[]}static \u0275fac=function(o){return new(o||e)(oe(re),oe(zf),oe(Ml))};static \u0275prov=X({token:e,factory:e.\u0275fac})}return e})(),zf=(()=>{class e{_applications=new Map;registerApplication(t,o){this._applications.set(t,o)}unregisterApplication(t){this._applications.delete(t)}unregisterAllApplications(){this._applications.clear()}getTestability(t){return this._applications.get(t)||null}getAllTestabilities(){return Array.from(this._applications.values())}getAllRootElements(){return Array.from(this._applications.keys())}findTestabilityInTree(t,o=!0){return Gf?.findTestabilityInTree(this,t,o)??null}static \u0275fac=function(o){return new(o||e)};static \u0275prov=X({token:e,factory:e.\u0275fac,providedIn:"platform"})}return e})();function Il(e){return!!e&&"function"==typeof e.then}function tb(e){return!!e&&"function"==typeof e.subscribe}const nb=new R("");let ob=(()=>{class e{resolve;reject;initialized=!1;done=!1;donePromise=new Promise((t,o)=>{this.resolve=t,this.reject=o});appInits=F(nb,{optional:!0})??[];injector=F(Vt);constructor(){}runInitializers(){if(this.initialized)return;const t=[];for(const i of this.appInits){const r=Mp(this.injector,i);if(Il(r))t.push(r);else if(tb(r)){const s=new Promise((a,l)=>{r.subscribe({complete:a,error:l})});t.push(s)}}const o=()=>{this.done=!0,this.resolve()};Promise.all(t).then(()=>{o()}).catch(i=>{this.reject(i)}),0===t.length&&o(),this.initialized=!0}static \u0275fac=function(o){return new(o||e)};static \u0275prov=X({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();const YR=new R("");function ib(e,n){return Array.isArray(n)?n.reduce(ib,e):{...e,...n}}let Jn=(()=>{class e{_runningTick=!1;_destroyed=!1;_destroyListeners=[];_views=[];internalErrorHandler=F(Dn);afterRenderManager=F(Lv);zonelessEnabled=F(lm);rootEffectScheduler=F(fm);dirtyFlags=0;tracingSnapshot=null;allTestViews=new Set;autoDetectTestViews=new Set;includeAllTestViews=!1;afterTick=new Xt;get allViews(){return[...(this.includeAllTestViews?this.allTestViews:this.autoDetectTestViews).keys(),...this._views]}get destroyed(){return this._destroyed}componentTypes=[];components=[];internalPendingTask=F(Eo);get isStable(){return this.internalPendingTask.hasPendingTasksObservable.pipe($u(t=>!t))}constructor(){F(Wr,{optional:!0})}whenStable(){let t;return new Promise(o=>{t=this.isStable.subscribe({next:i=>{i&&o()}})}).finally(()=>{t.unsubscribe()})}_injector=F(Lt);_rendererFactory=null;get injector(){return this._injector}bootstrap(t,o){return this.bootstrapImpl(t,o)}bootstrapImpl(t,o,i=Vt.NULL){return this._injector.get(re).run(()=>{fe(10);const s=t instanceof Vy;if(!this._injector.get(ob).done)throw new T(405,"");let l;l=s?t:this._injector.get(ul).resolveComponentFactory(t),this.componentTypes.push(l.componentType);const c=function KR(e){return e.isBoundToModule}(l)?void 0:this._injector.get(Fo),d=l.create(i,[],o||l.selector,c),g=d.location.nativeElement,h=d.injector.get(eb,null);return h?.registerApplication(g),d.onDestroy(()=>{this.detachView(d.hostView),Tl(this.components,d),h?.unregisterApplication(g)}),this._loadComponent(d),fe(11,d),d})}tick(){this.zonelessEnabled||(this.dirtyFlags|=1),this._tick()}_tick(){fe(12),null!==this.tracingSnapshot?this.tracingSnapshot.run(Bd.CHANGE_DETECTION,this.tickImpl):this.tickImpl()}tickImpl=()=>{if(this._runningTick)throw new T(101,!1);const t=z(null);try{this._runningTick=!0,this.synchronize()}finally{this._runningTick=!1,this.tracingSnapshot?.dispose(),this.tracingSnapshot=null,z(t),this.afterTick.next(),fe(13)}};synchronize(){null===this._rendererFactory&&!this._injector.destroyed&&(this._rendererFactory=this._injector.get(mf,null,{optional:!0}));let t=0;for(;0!==this.dirtyFlags&&t++<10;)fe(14),this.synchronizeOnce(),fe(15)}synchronizeOnce(){16&this.dirtyFlags&&(this.dirtyFlags&=-17,this.rootEffectScheduler.flush());let t=!1;if(7&this.dirtyFlags){const o=!!(1&this.dirtyFlags);this.dirtyFlags&=-8,this.dirtyFlags|=8;for(let{_lView:i}of this.allViews)(o||sa(i))&&(tl(i,o&&!this.zonelessEnabled?0:1),t=!0);if(this.dirtyFlags&=-5,this.syncDirtyFlagsWithViews(),23&this.dirtyFlags)return}t||(this._rendererFactory?.begin?.(),this._rendererFactory?.end?.()),8&this.dirtyFlags&&(this.dirtyFlags&=-9,this.afterRenderManager.execute()),this.syncDirtyFlagsWithViews()}syncDirtyFlagsWithViews(){this.allViews.some(({_lView:t})=>sa(t))?this.dirtyFlags|=2:this.dirtyFlags&=-8}attachView(t){const o=t;this._views.push(o),o.attachToAppRef(this)}detachView(t){const o=t;Tl(this._views,o),o.detachFromAppRef()}_loadComponent(t){this.attachView(t.hostView);try{this.tick()}catch(i){this.internalErrorHandler(i)}this.components.push(t),this._injector.get(YR,[]).forEach(i=>i(t))}ngOnDestroy(){if(!this._destroyed)try{this._destroyListeners.forEach(t=>t()),this._views.slice().forEach(t=>t.destroy())}finally{this._destroyed=!0,this._views=[],this._destroyListeners=[]}}onDestroy(t){return this._destroyListeners.push(t),()=>Tl(this._destroyListeners,t)}destroy(){if(this._destroyed)throw new T(406,!1);const t=this._injector;t.destroy&&!t.destroyed&&t.destroy()}get viewCount(){return this._views.length}static \u0275fac=function(o){return new(o||e)};static \u0275prov=X({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function Tl(e,n){const t=e.indexOf(n);t>-1&&e.splice(t,1)}function ct(e,n,t,o){const i=w();return De(i,mt(),n)&&(K(),function rN(e,n,t,o,i,r){const s=Le(e,n);!function Qa(e,n,t,o,i,r,s){if(null==r)e.removeAttribute(n,i,t);else{const a=null==s?Z(r):s(r,o||"",i);e.setAttribute(n,i,a,t)}}(n[Y],s,r,e.value,t,o,i)}(on(),i,e,n,t,o)),ct}typeof document<"u"&&document;class Zk{destroy(n){}updateValue(n,t){}swap(n,t){const o=Math.min(n,t),i=Math.max(n,t),r=this.detach(i);if(i-o>1){const s=this.detach(o);this.attach(o,r),this.attach(i,s)}else this.attach(o,r)}move(n,t){this.attach(t,this.detach(n))}}function nh(e,n,t,o,i){return e===t&&Object.is(n,o)?1:Object.is(i(e,n),i(t,o))?-1:0}function oh(e,n,t,o){return!(void 0===n||!n.has(o)||(e.attach(t,n.get(o)),n.delete(o),0))}function _b(e,n,t,o,i){if(oh(e,n,o,t(o,i)))e.updateValue(o,i);else{const r=e.create(o,i);e.attach(o,r)}}function vb(e,n,t,o){const i=new Set;for(let r=n;r<=t;r++)i.add(o(r,e.at(r)));return i}class yb{kvMap=new Map;_vMap=void 0;has(n){return this.kvMap.has(n)}delete(n){if(!this.has(n))return!1;const t=this.kvMap.get(n);return void 0!==this._vMap&&this._vMap.has(t)?(this.kvMap.set(n,this._vMap.get(t)),this._vMap.delete(t)):this.kvMap.delete(n),!0}get(n){return this.kvMap.get(n)}set(n,t){if(this.kvMap.has(n)){let o=this.kvMap.get(n);void 0===this._vMap&&(this._vMap=new Map);const i=this._vMap;for(;i.has(o);)o=i.get(o);i.set(o,t)}else this.kvMap.set(n,t)}forEach(n){for(let[t,o]of this.kvMap)if(n(o,t),void 0!==this._vMap){const i=this._vMap;for(;i.has(o);)o=i.get(o),n(o,t)}}}function y(e,n,t,o,i,r,s,a){nt("NgControlFlow");const l=w(),c=K();return Lo(l,c,e,n,t,o,i,tt(c.consts,r),256,s,a),ih}function ih(e,n,t,o,i,r,s,a){nt("NgControlFlow");const l=w(),c=K();return Lo(l,c,e,n,t,o,i,tt(c.consts,r),512,s,a),ih}function C(e,n){nt("NgControlFlow");const t=w(),o=mt(),i=t[o]!==ce?t[o]:-1,r=-1!==i?Ll(t,H+i):void 0;if(De(t,o,e)){const a=z(null);try{if(void 0!==r&&rf(r,0),-1!==e){const l=H+e,c=Ll(t,l),u=rh(t[1],l),d=null;xi(c,Ni(t,u,n,{dehydratedView:d}),0,Oo(u,d))}}finally{z(a)}}else if(void 0!==r){const a=fy(r,0);void 0!==a&&(a[8]=n)}}class Qk{lContainer;$implicit;$index;constructor(n,t,o){this.lContainer=n,this.$implicit=t,this.$index=o}get $count(){return this.lContainer.length-10}}function Ye(e,n){return n}class Jk{hasEmptyBlock;trackByFn;liveCollection;constructor(n,t,o){this.hasEmptyBlock=n,this.trackByFn=t,this.liveCollection=o}}function Qe(e,n,t,o,i,r,s,a,l,c,u,d,g){nt("NgControlFlow");const h=w(),p=K(),b=void 0!==l,I=w(),N=a?s.bind(I[15][8]):s,E=new Jk(b,N);I[H+e]=E,Lo(h,p,e+1,n,t,o,i,tt(p.consts,r),256),b&&Lo(h,p,e+2,l,c,u,d,tt(p.consts,g),512)}class Xk extends Zk{lContainer;hostLView;templateTNode;operationsCounter=void 0;needsIndexUpdate=!1;constructor(n,t,o){super(),this.lContainer=n,this.hostLView=t,this.templateTNode=o}get length(){return this.lContainer.length-10}at(n){return this.getLView(n)[8].$implicit}attach(n,t){const o=t[6];this.needsIndexUpdate||=n!==this.length,xi(this.lContainer,t,n,Oo(this.templateTNode,o)),function eF(e,n){if(e.length<=10)return;const o=e[10+n],i=o?o[26]:void 0;o&&i&&i.detachedLeaveAnimationFns&&i.detachedLeaveAnimationFns.length>0&&(function UA(e,n){const t=e.get(Ga);if(n.detachedLeaveAnimationFns){for(const o of n.detachedLeaveAnimationFns)t.queue.delete(o);n.detachedLeaveAnimationFns=void 0}}(o[9],i),Ao.delete(o),i.detachedLeaveAnimationFns=void 0)}(this.lContainer,n)}detach(n){return this.needsIndexUpdate||=n!==this.length-1,function tF(e,n){if(e.length<=10)return;const o=e[10+n],i=o?o[26]:void 0;i&&i.leave&&i.leave.size>0&&(i.detachedLeaveAnimationFns=[])}(this.lContainer,n),function nF(e,n){return Yr(e,n)}(this.lContainer,n)}create(n,t){const i=Ni(this.hostLView,this.templateTNode,new Qk(this.lContainer,t,n),{dehydratedView:null});return this.operationsCounter?.recordCreate(),i}destroy(n){qr(n[1],n),this.operationsCounter?.recordDestroy()}updateValue(n,t){this.getLView(n)[8].$implicit=t}reset(){this.needsIndexUpdate=!1,this.operationsCounter?.reset()}updateIndexes(){if(this.needsIndexUpdate)for(let n=0;n{e.destroy(l)})}(l,e,r.trackByFn),l.updateIndexes(),r.hasEmptyBlock){const c=mt(),u=0===l.length;if(De(o,c,u)){const d=t+2,g=Ll(o,d);if(u){const h=rh(i,d),p=null;xi(g,Ni(o,h,void 0,{dehydratedView:p}),0,Oo(h,p))}else i.firstUpdatePass&&function al(e){const n=e[6]??[],o=e[3][Y],i=[];for(const r of n)void 0!==r.data.di?i.push(r):xy(r,o);e[6]=i}(g),rf(g,0)}}}finally{z(n)}}function Ll(e,n){return e[n]}function rh(e,n){return li(e,n)}function A(e,n,t){const o=w();return De(o,mt(),n)&&(K(),Xd(on(),o,e,n,o[Y],t)),A}function sh(e,n,t,o,i){Xa(n,e,t,i?"class":"style",o)}function v(e,n,t,o){const i=w(),r=i[1],s=e+H,a=r.firstCreatePass?yf(s,i,2,n,tf,xu(),t,o):r.data[s];if(function Ka(e,n,t,o,i){const r=H+t,s=n[1],a=i(s,n,e,o,t);n[r]=a,vn(e,!0);const l=2===e.type;return l?(gv(n[Y],a,e),(0===function ET(){return G.lFrame.elementDepthCount}()||si(e))&&vt(a,n),function MT(){G.lFrame.elementDepthCount++}()):vt(a,n),ua()&&(!l||!Sa(e))&&Kd(s,n,a,e),e}(a,i,e,n,ch),si(a)){const l=i[1];Ya(l,i,a),Ed(l,a,i)}return null!=o&&Ai(i,a),v}function _(){const e=K(),t=Ja(q());return e.firstCreatePass&&Cf(e,t),function Bp(e){return G.skipHydrationRootTNode===e}(t)&&function jp(){G.skipHydrationRootTNode=null}(),function Vp(){G.lFrame.elementDepthCount--}(),null!=t.classesWithoutHost&&function q0(e){return!!(8&e.flags)}(t)&&sh(e,t,w(),t.classesWithoutHost,!0),null!=t.stylesWithoutHost&&function Z0(e){return!!(16&e.flags)}(t)&&sh(e,t,w(),t.stylesWithoutHost,!1),_}function O(e,n,t,o){return v(e,n,t,o),_(),O}let ch=(e,n,t,o,i)=>(wr(!0),Va(n[Y],o,function LT(){return G.lFrame.currentNamespace}()));function ue(){return w()}const Hl="en-US";let Sb=Hl;function U(e,n,t){const o=w(),i=K(),r=q();return ph(i,o,o[Y],r,e,n,t),U}function ph(e,n,t,o,i,r,s){let a=!0,l=null;if((3&o.type||s)&&(l??=as(o,n,r),function qy(e,n,t,o,i,r,s,a){const l=si(e);let c=!1,u=null;if(!o&&l&&(u=function LO(e,n,t,o){const i=e.cleanup;if(null!=i)for(let r=0;rl?a[l]:null}"string"==typeof s&&(r+=2)}return null}(n,t,r,e.index)),null!==u)(u.__ngLastListenerFn__||u).__ngNextListenerFn__=s,u.__ngLastListenerFn__=s,c=!0;else{const d=Le(e,t),g=o?o(d):d,h=i.listen(g,r,a);(function FO(e){return e.startsWith("animation")||e.startsWith("transition")})(r)||Zy(o?b=>o($e(b[e.index])):e.index,n,t,r,a,h,!1)}return c}(o,e,n,s,t,i,r,l)&&(a=!1)),a){const c=o.outputs?.[i],u=o.hostDirectiveOutputs?.[i];if(u&&u.length)for(let d=0;d0;)n=n[14],e--;return n}(e,G.lFrame.contextLView))[8]}(e)}function Zb(e,n,t,o){!function aC(e,n,t,o){const i=K();if(i.firstCreatePass){const r=q();lC(i,new oC(n,t,o),r.index),function ux(e,n){const t=e.contentQueries||(e.contentQueries=[]);n!==(t.length?t[t.length-1]:-1)&&t.push(e.queries.length-1,n)}(i,e),!(2&~t)&&(i.staticContentQueries=!0)}return rC(i,w(),t)}(e,n,t,o)}function Ot(e,n,t){!function sC(e,n,t){const o=K();return o.firstCreatePass&&(lC(o,new oC(e,n,t),-1),!(2&~n)&&(o.staticViewQueries=!0)),rC(o,w(),n)}(e,n,t)}function wt(e){const n=w(),t=K(),o=Fu();ca(o+1);const i=Of(t,o);if(e.dirty&&function bT(e){return!(4&~e[2])}(n)===!(2&~i.metadata.flags)){if(null===i.matches)e.reset([]);else{const r=cC(n,o);e.reset(r,Xm),e.notifyOnChanges()}return!0}return!1}function Et(){return function Nf(e,n){return e[18].queries[n].queryList}(w(),Fu())}function $l(e,n){return e<<17|n<<2}function zo(e){return e>>17&32767}function mh(e){return 2|e}function Wi(e){return(131068&e)>>2}function _h(e,n){return-131069&e|n<<2}function vh(e){return 1|e}function Yb(e,n,t,o){const i=e[t+1],r=null===n;let s=o?zo(i):Wi(i),a=!1;for(;0!==s&&(!1===a||r);){const c=e[s+1];lL(e[s],n)&&(a=!0,e[s+1]=o?vh(c):mh(c)),s=o?zo(c):Wi(c)}a&&(e[t+1]=o?mh(i):vh(i))}function lL(e,n){return null===e||null==n||(Array.isArray(e)?e[1]:e)===n||!(!Array.isArray(e)||"string"!=typeof n)&&_r(e,n)>=0}const Be={textEnd:0,key:0,keyEnd:0,value:0,valueEnd:0};function Qb(e){return e.substring(Be.key,Be.keyEnd)}function Kb(e,n){const t=Be.textEnd;return t===n?-1:(n=Be.keyEnd=function fL(e,n,t){for(;n32;)n++;return n}(e,Be.key=n,t),qi(e,n,t))}function qi(e,n,t){for(;n=0;t=Kb(n,t))Xs(e,Qb(n),!0)}function nD(e,n,t,o){const i=w(),r=K(),s=Cn(2);r.firstUpdatePass&&rD(r,e,s,o),n!==ce&&De(i,s,n)&&aD(r,r.data[qe()],i,i[Y],e,i[s+1]=function ML(e,n){return null==e||""===e||("string"==typeof n?e+=n:"object"==typeof e&&(e=gt(Gn(e)))),e}(n,t),o,s)}function iD(e,n){return n>=e.expandoStartIndex}function rD(e,n,t,o){const i=e.data;if(null===i[t+1]){const r=i[qe()],s=iD(e,t);cD(r,o)&&null===n&&!s&&(n=!1),n=function vL(e,n,t,o){const i=function ku(e){const n=G.lFrame.currentDirectiveIndex;return-1===n?null:e[n]}(e);let r=o?n.residualClasses:n.residualStyles;if(null===i)0===(o?n.classBindings:n.styleBindings)&&(t=ys(t=yh(null,e,n,t,o),n.attrs,o),r=null);else{const s=n.directiveStylingLast;if(-1===s||e[s]!==i)if(t=yh(i,e,n,t,o),null===r){let l=function yL(e,n,t){const o=t?n.classBindings:n.styleBindings;if(0!==Wi(o))return e[zo(o)]}(e,n,o);void 0!==l&&Array.isArray(l)&&(l=yh(null,e,n,l[1],o),l=ys(l,n.attrs,o),function CL(e,n,t,o){e[zo(t?n.classBindings:n.styleBindings)]=o}(e,n,o,l))}else r=function bL(e,n,t){let o;const i=n.directiveEnd;for(let r=1+n.directiveStylingLast;r0)&&(c=!0)):u=t,i)if(0!==l){const g=zo(e[a+1]);e[o+1]=$l(g,a),0!==g&&(e[g+1]=_h(e[g+1],o)),e[a+1]=function iL(e,n){return 131071&e|n<<17}(e[a+1],o)}else e[o+1]=$l(a,0),0!==a&&(e[a+1]=_h(e[a+1],o)),a=o;else e[o+1]=$l(l,0),0===a?a=o:e[l+1]=_h(e[l+1],o),l=o;c&&(e[o+1]=mh(e[o+1])),Yb(e,u,o,!0),Yb(e,u,o,!1),function aL(e,n,t,o,i){const r=i?e.residualClasses:e.residualStyles;null!=r&&"string"==typeof n&&_r(r,n)>=0&&(t[o+1]=vh(t[o+1]))}(n,u,e,o,r),s=$l(a,l),r?n.classBindings=s:n.styleBindings=s}(i,r,n,t,s,o)}}function yh(e,n,t,o,i){let r=null;const s=t.directiveEnd;let a=t.directiveStylingLast;for(-1===a?a=t.directiveStart:a++;a0;){const l=e[i],c=Array.isArray(l),u=c?l[1]:l,d=null===u;let g=t[i+1];g===ce&&(g=d?_e:void 0);let h=d?fu(g,o):u===o?g:void 0;if(c&&!Gl(h)&&(h=fu(l,o)),Gl(h)&&(a=h,s))return a;const p=e[i+1];i=s?zo(p):Wi(p)}if(null!==n){let l=r?n.residualClasses:n.residualStyles;null!=l&&(a=fu(l,o))}return a}function Gl(e){return void 0!==e}function cD(e,n){return!!(e.flags&(n?8:16))}function D(e,n=""){const t=w(),o=K(),i=e+H,r=o.firstCreatePass?xo(o,i,1,n,null):o.data[i],s=uD(o,t,r,n,e);t[i]=s,ua()&&Kd(o,t,s,r),vn(r,!1)}let uD=(e,n,t,o,i)=>(wr(!0),function xd(e,n){return e.createText(n)}(n[Y],o));function fD(e,n,t,o=""){return De(e,mt(),t)?n+Z(t)+o:ce}function k(e){return P("",e),k}function P(e,n,t){const o=w(),i=fD(o,e,n,t);return i!==ce&&function Nn(e,n,t){const o=ai(n,e);!function uv(e,n,t){e.setValue(n,t)}(e[Y],o,t)}(o,qe(),i),P}function je(e,n,t){am(n)&&(n=n());const o=w();return De(o,mt(),n)&&(K(),Xd(on(),o,e,n,o[Y],t)),je}function ye(e,n){const t=am(e);return t&&e.set(n),t}function Ge(e,n){const t=w(),o=K(),i=q();return ph(o,t,t[Y],i,e,n),Ge}function On(e){return De(w(),mt(),e)?Z(e):ce}function Ut(e,n,t=""){return fD(w(),e,n,t)}function Ch(e,n,t,o,i){if(e=Q(e),Array.isArray(e))for(let r=0;r>20;if(fn(e)||!e.multi){const h=new Or(c,i,x,null),p=Dh(l,n,i?u:u+g,d);-1===p?(Ku(va(a,s),r,l),bh(r,e,n.length),n.push(l),a.directiveStart++,a.directiveEnd++,i&&(a.providerIndexes+=1048576),t.push(h),s.push(h)):(t[p]=h,s[p]=h)}else{const h=Dh(l,n,u+g,d),p=Dh(l,n,u,u+g),I=p>=0&&t[p];if(i&&!I||!i&&!(h>=0&&t[h])){Ku(va(a,s),r,l);const N=function jL(e,n,t,o,i){const s=new Or(e,t,x,null);return s.multi=[],s.index=n,s.componentProviders=0,ND(s,i,o&&!t),s}(i?BL:HL,t.length,i,o,c);!i&&I&&(t[p].providerFactory=N),bh(r,e,n.length,0),n.push(l),a.directiveStart++,a.directiveEnd++,i&&(a.providerIndexes+=1048576),t.push(N),s.push(N)}else bh(r,e,h>-1?h:p,ND(t[i?p:h],c,!i&&o));!i&&o&&I&&t[p].componentProviders++}}}function bh(e,n,t,o){const i=fn(n),r=function Dp(e){return!!e.useClass}(n);if(i||r){const l=(r?Q(n.useClass):n).prototype.ngOnDestroy;if(l){const c=e.destroyHooks||(e.destroyHooks=[]);if(!i&&n.multi){const u=c.indexOf(t);-1===u?c.push(t,[o,l]):c[u+1].push(o,l)}else c.push(t,l)}}}function ND(e,n,t){return t&&e.componentProviders++,e.multi.push(n)-1}function Dh(e,n,t,o){for(let i=t;i{t.providersResolver=(o,i)=>function VL(e,n,t){const o=K();if(o.firstCreatePass){const i=At(e);Ch(t,o.data,o.blueprint,i,!0),Ch(n,o.data,o.blueprint,i,!1)}}(o,i?i(e):e,n)}}function Zi(e,n,t,o){return function xD(e,n,t,o,i,r){const s=n+t;return De(e,s,i)?an(e,s+1,r?o.call(r,i):o(i)):Cs(e,s+1)}(w(),lt(),e,n,t,o)}function Eh(e,n,t,o,i){return function RD(e,n,t,o,i,r,s){const a=n+t;return ko(e,a,i,r)?an(e,a+2,s?o.call(s,i,r):o(i,r)):Cs(e,a+2)}(w(),lt(),e,n,t,o,i)}function Ne(e,n,t,o,i,r){return kD(w(),lt(),e,n,t,o,i,r)}function Cs(e,n){const t=e[n];return t===ce?void 0:t}function kD(e,n,t,o,i,r,s,a){const l=n+t;return function gl(e,n,t,o,i){const r=ko(e,n,t,o);return De(e,n+2,i)||r}(e,l,i,r,s)?an(e,l+3,a?o.call(a,i,r,s):o(i,r,s)):Cs(e,l+3)}let RP=(()=>{class e{zone=F(re);changeDetectionScheduler=F(fi);applicationRef=F(Jn);applicationErrorHandler=F(Dn);_onMicrotaskEmptySubscription;initialize(){this._onMicrotaskEmptySubscription||(this._onMicrotaskEmptySubscription=this.zone.onMicrotaskEmpty.subscribe({next:()=>{this.changeDetectionScheduler.runningTick||this.zone.run(()=>{try{this.applicationRef.dirtyFlags|=1,this.applicationRef._tick()}catch(t){this.applicationErrorHandler(t)}})}}))}ngOnDestroy(){this._onMicrotaskEmptySubscription?.unsubscribe()}static \u0275fac=function(o){return new(o||e)};static \u0275prov=X({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();function nw({ngZoneFactory:e,ignoreChangesOutsideZone:n,scheduleInRootZone:t}){return e??=()=>new re({...Ah(),scheduleInRootZone:t}),[{provide:re,useFactory:e},{provide:mo,multi:!0,useFactory:()=>{const o=F(RP,{optional:!0});return()=>o.initialize()}},{provide:mo,multi:!0,useFactory:()=>{const o=F(FP);return()=>{o.initialize()}}},!0===n?{provide:cm,useValue:!0}:[],{provide:um,useValue:t??Nv},{provide:Dn,useFactory:()=>{const o=F(re),i=F(Lt);let r;return s=>{o.runOutsideAngular(()=>{i.destroyed&&!r?setTimeout(()=>{throw s}):(r??=i.get(di),r.handleError(s))})}}}]}function Ah(e){return{enableLongStackTrace:!1,shouldCoalesceEventChangeDetection:e?.eventCoalescing??!1,shouldCoalesceRunChangeDetection:e?.runCoalescing??!1}}let FP=(()=>{class e{subscription=new It;initialized=!1;zone=F(re);pendingTasks=F(Eo);initialize(){if(this.initialized)return;this.initialized=!0;let t=null;!this.zone.isStable&&!this.zone.hasPendingMacrotasks&&!this.zone.hasPendingMicrotasks&&(t=this.pendingTasks.add()),this.zone.runOutsideAngular(()=>{this.subscription.add(this.zone.onStable.subscribe(()=>{re.assertNotInAngularZone(),queueMicrotask(()=>{null!==t&&!this.zone.hasPendingMacrotasks&&!this.zone.hasPendingMicrotasks&&(this.pendingTasks.remove(t),t=null)})}))}),this.subscription.add(this.zone.onUnstable.subscribe(()=>{re.assertInAngularZone(),t??=this.pendingTasks.add()}))}ngOnDestroy(){this.subscription.unsubscribe()}static \u0275fac=function(o){return new(o||e)};static \u0275prov=X({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})(),rw=(()=>{class e{applicationErrorHandler=F(Dn);appRef=F(Jn);taskService=F(Eo);ngZone=F(re);zonelessEnabled=F(lm);tracing=F(Wr,{optional:!0});disableScheduling=F(cm,{optional:!0})??!1;zoneIsDefined=typeof Zone<"u"&&!!Zone.root.run;schedulerTickApplyArgs=[{data:{__scheduler_tick__:!0}}];subscriptions=new It;angularZoneId=this.zoneIsDefined?this.ngZone._inner?.get(za):null;scheduleInRootZone=!this.zonelessEnabled&&this.zoneIsDefined&&(F(um,{optional:!0})??!1);cancelScheduledCallback=null;useMicrotaskScheduler=!1;runningTick=!1;pendingRenderTaskId=null;constructor(){this.subscriptions.add(this.appRef.afterTick.subscribe(()=>{this.runningTick||this.cleanup()})),this.subscriptions.add(this.ngZone.onUnstable.subscribe(()=>{this.runningTick||this.cleanup()})),this.disableScheduling||=!this.zonelessEnabled&&(this.ngZone instanceof zd||!this.zoneIsDefined)}notify(t){if(!this.zonelessEnabled&&5===t)return;let o=!1;switch(t){case 0:this.appRef.dirtyFlags|=2;break;case 3:case 2:case 4:case 5:case 1:this.appRef.dirtyFlags|=4;break;case 6:case 13:this.appRef.dirtyFlags|=2,o=!0;break;case 12:this.appRef.dirtyFlags|=16,o=!0;break;case 11:o=!0;break;default:this.appRef.dirtyFlags|=8}if(this.appRef.tracingSnapshot=this.tracing?.snapshot(this.appRef.tracingSnapshot)??null,!this.shouldScheduleTick(o))return;const i=this.useMicrotaskScheduler?xv:Ov;this.pendingRenderTaskId=this.taskService.add(),this.cancelScheduledCallback=this.scheduleInRootZone?Zone.root.run(()=>i(()=>this.tick())):this.ngZone.runOutsideAngular(()=>i(()=>this.tick()))}shouldScheduleTick(t){return!(this.disableScheduling&&!t||this.appRef.destroyed||null!==this.pendingRenderTaskId||this.runningTick||this.appRef._runningTick||!this.zonelessEnabled&&this.zoneIsDefined&&Zone.current.get(za+this.angularZoneId))}tick(){if(this.runningTick||this.appRef.destroyed)return;if(0===this.appRef.dirtyFlags)return void this.cleanup();!this.zonelessEnabled&&7&this.appRef.dirtyFlags&&(this.appRef.dirtyFlags|=1);const t=this.taskService.add();try{this.ngZone.run(()=>{this.runningTick=!0,this.appRef._tick()},void 0,this.schedulerTickApplyArgs)}catch(o){this.taskService.remove(t),this.applicationErrorHandler(o)}finally{this.cleanup()}this.useMicrotaskScheduler=!0,xv(()=>{this.useMicrotaskScheduler=!1,this.taskService.remove(t)})}ngOnDestroy(){this.subscriptions.unsubscribe(),this.cleanup()}cleanup(){if(this.runningTick=!1,this.cancelScheduledCallback?.(),this.cancelScheduledCallback=null,null!==this.pendingRenderTaskId){const t=this.pendingRenderTaskId;this.pendingRenderTaskId=null,this.taskService.remove(t)}}static \u0275fac=function(o){return new(o||e)};static \u0275prov=X({token:e,factory:e.\u0275fac,providedIn:"root"})}return e})();const to=new R("",{providedIn:"root",factory:()=>F(to,{optional:!0,skipSelf:!0})||function LP(){return typeof $localize<"u"&&$localize.locale||Hl}()});new R("").__NG_ELEMENT_ID__=e=>{const n=q();if(null===n)throw new T(204,!1);if(2&n.type)return n.value;if(8&e)return null;throw new T(204,!1)};const Jl=new R(""),YP=new R("");function ws(e){return!e.moduleRef}let mw;function _w(){mw=QP}function QP(e,n){const t=e.injector.get(Jn);if(e._bootstrapComponents.length>0)e._bootstrapComponents.forEach(o=>t.bootstrap(o));else{if(!e.instance.ngDoBootstrap)throw new T(-403,!1);e.instance.ngDoBootstrap(t)}n.push(e)}let vw=(()=>{class e{_injector;_modules=[];_destroyListeners=[];_destroyed=!1;constructor(t){this._injector=t}bootstrapModuleFactory(t,o){const i=o?.scheduleInRootZone,s=o?.ignoreChangesOutsideZone,a=[nw({ngZoneFactory:()=>function VA(e="zone.js",n){return"noop"===e?new zd:"zone.js"===e?new re(n):e}(o?.ngZone,{...Ah({eventCoalescing:o?.ngZoneEventCoalescing,runCoalescing:o?.ngZoneRunCoalescing}),scheduleInRootZone:i}),ignoreChangesOutsideZone:s}),{provide:fi,useExisting:rw},VT],l=function bx(e,n,t){return new kf(e,n,t,!1)}(t.moduleType,this.injector,a);return _w(),function pw(e){const n=ws(e)?e.r3Injector:e.moduleRef.injector,t=n.get(re);return t.run(()=>{ws(e)?e.r3Injector.resolveInjectorInitializers():e.moduleRef.resolveInjectorInitializers();const o=n.get(Dn);let i;if(t.runOutsideAngular(()=>{i=t.onError.subscribe({next:o})}),ws(e)){const r=()=>n.destroy(),s=e.platformInjector.get(Jl);s.add(r),n.onDestroy(()=>{i.unsubscribe(),s.delete(r)})}else{const r=()=>e.moduleRef.destroy(),s=e.platformInjector.get(Jl);s.add(r),e.moduleRef.onDestroy(()=>{Tl(e.allPlatformModules,e.moduleRef),i.unsubscribe(),s.delete(r)})}return function KP(e,n,t){try{const o=t();return Il(o)?o.catch(i=>{throw n.runOutsideAngular(()=>e(i)),i}):o}catch(o){throw n.runOutsideAngular(()=>e(o)),o}}(o,t,()=>{const r=n.get(Eo),s=r.add(),a=n.get(ob);return a.runInitializers(),a.donePromise.then(()=>{if(function fF(e){"string"==typeof e&&(Sb=e.toLowerCase().replace(/_/g,"-"))}(n.get(to,Hl)||Hl),!n.get(YP,!0))return ws(e)?n.get(Jn):(e.allPlatformModules.push(e.moduleRef),e.moduleRef);if(ws(e)){const u=n.get(Jn);return void 0!==e.rootComponent&&u.bootstrap(e.rootComponent),u}return mw?.(e.moduleRef,e.allPlatformModules),e.moduleRef}).finally(()=>{r.remove(s)})})})}({moduleRef:l,allPlatformModules:this._modules,platformInjector:this.injector})}bootstrapModule(t,o=[]){const i=ib({},o);return _w(),function GP(e,n,t){const o=new gC(t);return Promise.resolve(o)}(0,0,t).then(r=>this.bootstrapModuleFactory(r,i))}onDestroy(t){this._destroyListeners.push(t)}get injector(){return this._injector}destroy(){if(this._destroyed)throw new T(404,!1);this._modules.slice().forEach(o=>o.destroy()),this._destroyListeners.forEach(o=>o());const t=this._injector.get(Jl,null);t&&(t.forEach(o=>o()),t.clear()),this._destroyed=!0}get destroyed(){return this._destroyed}static \u0275fac=function(o){return new(o||e)(oe(Vt))};static \u0275prov=X({token:e,factory:e.\u0275fac,providedIn:"platform"})}return e})(),Ki=null;function yw(e,n,t=[]){const o=`Platform: ${n}`,i=new R(o);return(r=[])=>{let s=Xl();if(!s){const a=[...t,...r,{provide:i,useValue:!0}];s=e?.(a)??function JP(e){if(Xl())throw new T(400,!1);(function QR(){!function CI(e){Wg=e}(()=>{throw new T(600,"")})})(),Ki=e;const n=e.get(vw);return function bw(e){const n=e.get(y_,null);Mp(e,()=>{n?.forEach(t=>t())})}(e),n}(function Cw(e=[],n){return Vt.create({name:n,providers:[{provide:vu,useValue:"platform"},{provide:Jl,useValue:new Set([()=>Ki=null])},...e]})}(a,o))}return function XP(){const n=Xl();if(!n)throw new T(-401,!1);return n}()}}function Xl(){return Ki?.get(vw)??null}let Es=(()=>class e{static __NG_ELEMENT_ID__=t2})();function t2(e){return function n2(e,n,t){if(pn(e)&&!t){const o=at(e.index,n);return new Qr(o,o)}return 175&e.type?new Qr(n[15],n):null}(q(),w(),!(16&~e))}class Iw{constructor(){}supports(n){return n instanceof Map||bf(n)}create(){return new a2}}class a2{_records=new Map;_mapHead=null;_appendAfter=null;_previousMapHead=null;_changesHead=null;_changesTail=null;_additionsHead=null;_additionsTail=null;_removalsHead=null;_removalsTail=null;get isDirty(){return null!==this._additionsHead||null!==this._changesHead||null!==this._removalsHead}forEachItem(n){let t;for(t=this._mapHead;null!==t;t=t._next)n(t)}forEachPreviousItem(n){let t;for(t=this._previousMapHead;null!==t;t=t._nextPrevious)n(t)}forEachChangedItem(n){let t;for(t=this._changesHead;null!==t;t=t._nextChanged)n(t)}forEachAddedItem(n){let t;for(t=this._additionsHead;null!==t;t=t._nextAdded)n(t)}forEachRemovedItem(n){let t;for(t=this._removalsHead;null!==t;t=t._nextRemoved)n(t)}diff(n){if(n){if(!(n instanceof Map||bf(n)))throw new T(900,!1)}else n=new Map;return this.check(n)?this:null}onDestroy(){}check(n){this._reset();let t=this._mapHead;if(this._appendAfter=null,this._forEach(n,(o,i)=>{if(t&&t.key===i)this._maybeAddToChanges(t,o),this._appendAfter=t,t=t._next;else{const r=this._getOrCreateRecordForKey(i,o);t=this._insertBeforeOrAppend(t,r)}}),t){t._prev&&(t._prev._next=null),this._removalsHead=t;for(let o=t;null!==o;o=o._nextRemoved)o===this._mapHead&&(this._mapHead=null),this._records.delete(o.key),o._nextRemoved=o._next,o.previousValue=o.currentValue,o.currentValue=null,o._prev=null,o._next=null}return this._changesTail&&(this._changesTail._nextChanged=null),this._additionsTail&&(this._additionsTail._nextAdded=null),this.isDirty}_insertBeforeOrAppend(n,t){if(n){const o=n._prev;return t._next=n,t._prev=o,n._prev=t,o&&(o._next=t),n===this._mapHead&&(this._mapHead=t),this._appendAfter=n,n}return this._appendAfter?(this._appendAfter._next=t,t._prev=this._appendAfter):this._mapHead=t,this._appendAfter=t,null}_getOrCreateRecordForKey(n,t){if(this._records.has(n)){const i=this._records.get(n);this._maybeAddToChanges(i,t);const r=i._prev,s=i._next;return r&&(r._next=s),s&&(s._prev=r),i._next=null,i._prev=null,i}const o=new l2(n);return this._records.set(n,o),o.currentValue=t,this._addToAdditions(o),o}_reset(){if(this.isDirty){let n;for(this._previousMapHead=this._mapHead,n=this._previousMapHead;null!==n;n=n._next)n._nextPrevious=n._next;for(n=this._changesHead;null!==n;n=n._nextChanged)n.previousValue=n.currentValue;for(n=this._additionsHead;null!=n;n=n._nextAdded)n.previousValue=n.currentValue;this._changesHead=this._changesTail=null,this._additionsHead=this._additionsTail=null,this._removalsHead=null}}_maybeAddToChanges(n,t){Object.is(t,n.currentValue)||(n.previousValue=n.currentValue,n.currentValue=t,this._addToChanges(n))}_addToAdditions(n){null===this._additionsHead?this._additionsHead=this._additionsTail=n:(this._additionsTail._nextAdded=n,this._additionsTail=n)}_addToChanges(n){null===this._changesHead?this._changesHead=this._changesTail=n:(this._changesTail._nextChanged=n,this._changesTail=n)}_forEach(n,t){n instanceof Map?n.forEach(t):Object.keys(n).forEach(o=>t(n[o],o))}}class l2{key;previousValue=null;currentValue=null;_nextPrevious=null;_next=null;_prev=null;_nextAdded=null;_nextRemoved=null;_nextChanged=null;constructor(n){this.key=n}}function Sw(){return new ec([new Iw])}let ec=(()=>{class e{static \u0275prov=X({token:e,providedIn:"root",factory:Sw});factories;constructor(t){this.factories=t}static create(t,o){if(o){const i=o.factories.slice();t=t.concat(i)}return new e(t)}static extend(t){return{provide:e,useFactory:()=>{const o=F(e,{optional:!0,skipSelf:!0});return e.create(t,o||Sw())}}}find(t){const o=this.factories.find(i=>i.supports(t));if(o)return o;throw new T(901,!1)}}return e})();const d2=yw(null,"core",[]);let f2=(()=>{class e{constructor(t){}static \u0275fac=function(o){return new(o||e)(oe(Jn))};static \u0275mod=Qn({type:e});static \u0275inj=dn({})}return e})();function Pe(e){return function U2(e){const n=z(null);try{return e()}finally{z(n)}}(e)}function Zt(e,n){return function _I(e,n){const t=Object.create(vI);t.computation=e,void 0!==n&&(t.equal=n);const o=()=>{if(lr(t),Vs(t),t.value===Rn)throw t.error;return t.value};return o[We]=t,o}(e,n?.equal)}Error,Error;const Zh=/\s+/,iE=[];let Xi=(()=>{class e{_ngEl;_renderer;initialClasses=iE;rawClass;stateMap=new Map;constructor(t,o){this._ngEl=t,this._renderer=o}set klass(t){this.initialClasses=null!=t?t.trim().split(Zh):iE}set ngClass(t){this.rawClass="string"==typeof t?t.trim().split(Zh):t}ngDoCheck(){for(const o of this.initialClasses)this._updateState(o,!0);const t=this.rawClass;if(Array.isArray(t)||t instanceof Set)for(const o of t)this._updateState(o,!0);else if(null!=t)for(const o of Object.keys(t))this._updateState(o,!!t[o]);this._applyStateDiff()}_updateState(t,o){const i=this.stateMap.get(t);void 0!==i?(i.enabled!==o&&(i.changed=!0,i.enabled=o),i.touched=!0):this.stateMap.set(t,{enabled:o,changed:!0,touched:!0})}_applyStateDiff(){for(const t of this.stateMap){const o=t[0],i=t[1];i.changed?(this._toggleClass(o,i.enabled),i.changed=!1):i.touched||(i.enabled&&this._toggleClass(o,!1),this.stateMap.delete(o)),i.touched=!1}}_toggleClass(t,o){(t=t.trim()).length>0&&t.split(Zh).forEach(i=>{o?this._renderer.addClass(this._ngEl.nativeElement,i):this._renderer.removeClass(this._ngEl.nativeElement,i)})}static \u0275fac=function(o){return new(o||e)(x(Nt),x(Sn))};static \u0275dir=W({type:e,selectors:[["","ngClass",""]],inputs:{klass:[0,"class","klass"],ngClass:"ngClass"}})}return e})(),cE=(()=>{class e{_ngEl;_differs;_renderer;_ngStyle=null;_differ=null;constructor(t,o,i){this._ngEl=t,this._differs=o,this._renderer=i}set ngStyle(t){this._ngStyle=t,!this._differ&&t&&(this._differ=this._differs.find(t).create())}ngDoCheck(){if(this._differ){const t=this._differ.diff(this._ngStyle);t&&this._applyChanges(t)}}_setStyle(t,o){const[i,r]=t.split("."),s=-1===i.indexOf("-")?void 0:qn.DashCase;null!=o?this._renderer.setStyle(this._ngEl.nativeElement,i,r?`${o}${r}`:o,s):this._renderer.removeStyle(this._ngEl.nativeElement,i,s)}_applyChanges(t){t.forEachRemovedItem(o=>this._setStyle(o.key,null)),t.forEachAddedItem(o=>this._setStyle(o.key,o.currentValue)),t.forEachChangedItem(o=>this._setStyle(o.key,o.currentValue))}static \u0275fac=function(o){return new(o||e)(x(Nt),x(ec),x(Sn))};static \u0275dir=W({type:e,selectors:[["","ngStyle",""]],inputs:{ngStyle:"ngStyle"}})}return e})(),uE=(()=>{class e{_viewContainerRef;_viewRef=null;ngTemplateOutletContext=null;ngTemplateOutlet=null;ngTemplateOutletInjector=null;constructor(t){this._viewContainerRef=t}ngOnChanges(t){if(this._shouldRecreateView(t)){const o=this._viewContainerRef;if(this._viewRef&&o.remove(o.indexOf(this._viewRef)),!this.ngTemplateOutlet)return void(this._viewRef=null);const i=this._createContextForwardProxy();this._viewRef=o.createEmbeddedView(this.ngTemplateOutlet,i,{injector:this.ngTemplateOutletInjector??void 0})}}_shouldRecreateView(t){return!!t.ngTemplateOutlet||!!t.ngTemplateOutletInjector}_createContextForwardProxy(){return new Proxy({},{set:(t,o,i)=>!!this.ngTemplateOutletContext&&Reflect.set(this.ngTemplateOutletContext,o,i),get:(t,o,i)=>{if(this.ngTemplateOutletContext)return Reflect.get(this.ngTemplateOutletContext,o,i)}})}static \u0275fac=function(o){return new(o||e)(x(ln))};static \u0275dir=W({type:e,selectors:[["","ngTemplateOutlet",""]],inputs:{ngTemplateOutletContext:"ngTemplateOutletContext",ngTemplateOutlet:"ngTemplateOutlet",ngTemplateOutletInjector:"ngTemplateOutletInjector"},features:[En]})}return e})();let fE=(()=>{class e{transform(t,o,i){if(null==t)return null;if("string"!=typeof t&&!Array.isArray(t))throw function Qt(e,n){return new T(2100,!1)}();return t.slice(o,i)}static \u0275fac=function(o){return new(o||e)};static \u0275pipe=yt({name:"slice",type:e,pure:!1})}return e})(),hE=(()=>{class e{static \u0275fac=function(o){return new(o||e)};static \u0275mod=Qn({type:e});static \u0275inj=dn({})}return e})();class gE{_doc;constructor(n){this._doc=n}manager}let Xh=(()=>{class e extends gE{constructor(t){super(t)}supports(t){return!0}addEventListener(t,o,i,r){return t.addEventListener(o,i,r),()=>this.removeEventListener(t,o,i,r)}removeEventListener(t,o,i,r){return t.removeEventListener(o,i,r)}static \u0275fac=function(o){return new(o||e)(oe(Bn))};static \u0275prov=X({token:e,factory:e.\u0275fac})}return e})();const eg=new R("");let pE=(()=>{class e{_zone;_plugins;_eventNameToPlugin=new Map;constructor(t,o){this._zone=o,t.forEach(s=>{s.manager=this});const i=t.filter(s=>!(s instanceof Xh));this._plugins=i.slice().reverse();const r=t.find(s=>s instanceof Xh);r&&this._plugins.push(r)}addEventListener(t,o,i,r){return this._findPluginFor(o).addEventListener(t,o,i,r)}getZone(){return this._zone}_findPluginFor(t){let o=this._eventNameToPlugin.get(t);if(o)return o;if(o=this._plugins.find(r=>r.supports(t)),!o)throw new T(5101,!1);return this._eventNameToPlugin.set(t,o),o}static \u0275fac=function(o){return new(o||e)(oe(eg),oe(re))};static \u0275prov=X({token:e,factory:e.\u0275fac})}return e})();const tg="ng-app-id";function mE(e){for(const n of e)n.remove()}function _E(e,n){const t=n.createElement("style");return t.textContent=e,t}function ng(e,n){const t=n.createElement("link");return t.setAttribute("rel","stylesheet"),t.setAttribute("href",e),t}let vE=(()=>{class e{doc;appId;nonce;inline=new Map;external=new Map;hosts=new Set;constructor(t,o,i,r={}){this.doc=t,this.appId=o,this.nonce=i,function vH(e,n,t,o){const i=e.head?.querySelectorAll(`style[${tg}="${n}"],link[${tg}="${n}"]`);if(i)for(const r of i)r.removeAttribute(tg),r instanceof HTMLLinkElement?o.set(r.href.slice(r.href.lastIndexOf("/")+1),{usage:0,elements:[r]}):r.textContent&&t.set(r.textContent,{usage:0,elements:[r]})}(t,o,this.inline,this.external),this.hosts.add(t.head)}addStyles(t,o){for(const i of t)this.addUsage(i,this.inline,_E);o?.forEach(i=>this.addUsage(i,this.external,ng))}removeStyles(t,o){for(const i of t)this.removeUsage(i,this.inline);o?.forEach(i=>this.removeUsage(i,this.external))}addUsage(t,o,i){const r=o.get(t);r?r.usage++:o.set(t,{usage:1,elements:[...this.hosts].map(s=>this.addElement(s,i(t,this.doc)))})}removeUsage(t,o){const i=o.get(t);i&&(i.usage--,i.usage<=0&&(mE(i.elements),o.delete(t)))}ngOnDestroy(){for(const[,{elements:t}]of[...this.inline,...this.external])mE(t);this.hosts.clear()}addHost(t){this.hosts.add(t);for(const[o,{elements:i}]of this.inline)i.push(this.addElement(t,_E(o,this.doc)));for(const[o,{elements:i}]of this.external)i.push(this.addElement(t,ng(o,this.doc)))}removeHost(t){this.hosts.delete(t)}addElement(t,o){return this.nonce&&o.setAttribute("nonce",this.nonce),t.appendChild(o)}static \u0275fac=function(o){return new(o||e)(oe(Bn),oe(Pr),oe(b_,8),oe(C_))};static \u0275prov=X({token:e,factory:e.\u0275fac})}return e})();const og={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/",math:"http://www.w3.org/1998/Math/MathML"},ig=/%COMP%/g,EH=new R("",{providedIn:"root",factory:()=>!0});function CE(e,n){return n.map(t=>t.replace(ig,e))}let bE=(()=>{class e{eventManager;sharedStylesHost;appId;removeStylesOnCompDestroy;doc;ngZone;nonce;tracingService;rendererByCompId=new Map;defaultRenderer;platformIsServer;constructor(t,o,i,r,s,a,l=null,c=null){this.eventManager=t,this.sharedStylesHost=o,this.appId=i,this.removeStylesOnCompDestroy=r,this.doc=s,this.ngZone=a,this.nonce=l,this.tracingService=c,this.platformIsServer=!1,this.defaultRenderer=new rg(t,s,a,this.platformIsServer,this.tracingService)}createRenderer(t,o){if(!t||!o)return this.defaultRenderer;const i=this.getOrCreateRenderer(t,o);return i instanceof wE?i.applyToHost(t):i instanceof sg&&i.applyStyles(),i}getOrCreateRenderer(t,o){const i=this.rendererByCompId;let r=i.get(o.id);if(!r){const s=this.doc,a=this.ngZone,l=this.eventManager,c=this.sharedStylesHost,u=this.removeStylesOnCompDestroy,d=this.platformIsServer,g=this.tracingService;switch(o.encapsulation){case Mn.Emulated:r=new wE(l,c,o,this.appId,u,s,a,d,g);break;case Mn.ShadowDom:return new SH(l,c,t,o,s,a,this.nonce,d,g);default:r=new sg(l,c,o,u,s,a,d,g)}i.set(o.id,r)}return r}ngOnDestroy(){this.rendererByCompId.clear()}componentReplaced(t){this.rendererByCompId.delete(t)}static \u0275fac=function(o){return new(o||e)(oe(pE),oe(vE),oe(Pr),oe(EH),oe(Bn),oe(re),oe(b_),oe(Wr,8))};static \u0275prov=X({token:e,factory:e.\u0275fac})}return e})();class rg{eventManager;doc;ngZone;platformIsServer;tracingService;data=Object.create(null);throwOnSyntheticProps=!0;constructor(n,t,o,i,r){this.eventManager=n,this.doc=t,this.ngZone=o,this.platformIsServer=i,this.tracingService=r}destroy(){}destroyNode=null;createElement(n,t){return t?this.doc.createElementNS(og[t]||t,n):this.doc.createElement(n)}createComment(n){return this.doc.createComment(n)}createText(n){return this.doc.createTextNode(n)}appendChild(n,t){(DE(n)?n.content:n).appendChild(t)}insertBefore(n,t,o){n&&(DE(n)?n.content:n).insertBefore(t,o)}removeChild(n,t){t.remove()}selectRootElement(n,t){let o="string"==typeof n?this.doc.querySelector(n):n;if(!o)throw new T(-5104,!1);return t||(o.textContent=""),o}parentNode(n){return n.parentNode}nextSibling(n){return n.nextSibling}setAttribute(n,t,o,i){if(i){t=i+":"+t;const r=og[i];r?n.setAttributeNS(r,t,o):n.setAttribute(t,o)}else n.setAttribute(t,o)}removeAttribute(n,t,o){if(o){const i=og[o];i?n.removeAttributeNS(i,t):n.removeAttribute(`${o}:${t}`)}else n.removeAttribute(t)}addClass(n,t){n.classList.add(t)}removeClass(n,t){n.classList.remove(t)}setStyle(n,t,o,i){i&(qn.DashCase|qn.Important)?n.style.setProperty(t,o,i&qn.Important?"important":""):n.style[t]=o}removeStyle(n,t,o){o&qn.DashCase?n.style.removeProperty(t):n.style[t]=""}setProperty(n,t,o){null!=n&&(n[t]=o)}setValue(n,t){n.nodeValue=t}listen(n,t,o,i){if("string"==typeof n&&!(n=Mr().getGlobalEventTarget(this.doc,n)))throw new T(5102,!1);let r=this.decoratePreventDefault(o);return this.tracingService?.wrapEventListener&&(r=this.tracingService.wrapEventListener(n,t,r)),this.eventManager.addEventListener(n,t,r,i)}decoratePreventDefault(n){return t=>{if("__ngUnwrap__"===t)return n;!1===n(t)&&t.preventDefault()}}}function DE(e){return"TEMPLATE"===e.tagName&&void 0!==e.content}class SH extends rg{sharedStylesHost;hostEl;shadowRoot;constructor(n,t,o,i,r,s,a,l,c){super(n,r,s,l,c),this.sharedStylesHost=t,this.hostEl=o,this.shadowRoot=o.attachShadow({mode:"open"}),this.sharedStylesHost.addHost(this.shadowRoot);let u=i.styles;u=CE(i.id,u);for(const g of u){const h=document.createElement("style");a&&h.setAttribute("nonce",a),h.textContent=g,this.shadowRoot.appendChild(h)}const d=i.getExternalStyles?.();if(d)for(const g of d){const h=ng(g,r);a&&h.setAttribute("nonce",a),this.shadowRoot.appendChild(h)}}nodeOrShadowRoot(n){return n===this.hostEl?this.shadowRoot:n}appendChild(n,t){return super.appendChild(this.nodeOrShadowRoot(n),t)}insertBefore(n,t,o){return super.insertBefore(this.nodeOrShadowRoot(n),t,o)}removeChild(n,t){return super.removeChild(null,t)}parentNode(n){return this.nodeOrShadowRoot(super.parentNode(this.nodeOrShadowRoot(n)))}destroy(){this.sharedStylesHost.removeHost(this.shadowRoot)}}class sg extends rg{sharedStylesHost;removeStylesOnCompDestroy;styles;styleUrls;constructor(n,t,o,i,r,s,a,l,c){super(n,r,s,a,l),this.sharedStylesHost=t,this.removeStylesOnCompDestroy=i;let u=o.styles;this.styles=c?CE(c,u):u,this.styleUrls=o.getExternalStyles?.(c)}applyStyles(){this.sharedStylesHost.addStyles(this.styles,this.styleUrls)}destroy(){this.removeStylesOnCompDestroy&&0===Ao.size&&this.sharedStylesHost.removeStyles(this.styles,this.styleUrls)}}class wE extends sg{contentAttr;hostAttr;constructor(n,t,o,i,r,s,a,l,c){const u=i+"-"+o.id;super(n,t,o,r,s,a,l,c,u),this.contentAttr=function MH(e){return"_ngcontent-%COMP%".replace(ig,e)}(u),this.hostAttr=function IH(e){return"_nghost-%COMP%".replace(ig,e)}(u)}applyToHost(n){this.applyStyles(),this.setAttribute(n,this.hostAttr,"")}createElement(n,t){const o=super.createElement(n,t);return super.setAttribute(o,this.contentAttr,""),o}}class ag extends $T{supportsDOMEvents=!0;static makeCurrent(){!function UT(e){hm??=e}(new ag)}onAndCancel(n,t,o,i){return n.addEventListener(t,o,i),()=>{n.removeEventListener(t,o,i)}}dispatchEvent(n,t){n.dispatchEvent(t)}remove(n){n.remove()}createElement(n,t){return(t=t||this.getDefaultDocument()).createElement(n)}createHtmlDocument(){return document.implementation.createHTMLDocument("fakeTitle")}getDefaultDocument(){return document}isElementNode(n){return n.nodeType===Node.ELEMENT_NODE}isShadowRoot(n){return n instanceof DocumentFragment}getGlobalEventTarget(n,t){return"window"===t?window:"document"===t?n:"body"===t?n.body:null}getBaseHref(n){const t=function AH(){return Ts=Ts||document.head.querySelector("base"),Ts?Ts.getAttribute("href"):null}();return null==t?null:function NH(e){return new URL(e,document.baseURI).pathname}(t)}resetBaseElement(){Ts=null}getUserAgent(){return window.navigator.userAgent}getCookie(n){return function WT(e,n){n=encodeURIComponent(n);for(const t of e.split(";")){const o=t.indexOf("="),[i,r]=-1==o?[t,""]:[t.slice(0,o),t.slice(o+1)];if(i.trim()===n)return decodeURIComponent(r)}return null}(document.cookie,n)}}let Ts=null,xH=(()=>{class e{build(){return new XMLHttpRequest}static \u0275fac=function(o){return new(o||e)};static \u0275prov=X({token:e,factory:e.\u0275fac})}return e})();const EE=["alt","control","meta","shift"],RH={"\b":"Backspace","\t":"Tab","\x7f":"Delete","\x1b":"Escape",Del:"Delete",Esc:"Escape",Left:"ArrowLeft",Right:"ArrowRight",Up:"ArrowUp",Down:"ArrowDown",Menu:"ContextMenu",Scroll:"ScrollLock",Win:"OS"},kH={alt:e=>e.altKey,control:e=>e.ctrlKey,meta:e=>e.metaKey,shift:e=>e.shiftKey};let FH=(()=>{class e extends gE{constructor(t){super(t)}supports(t){return null!=e.parseEventName(t)}addEventListener(t,o,i,r){const s=e.parseEventName(o),a=e.eventCallback(s.fullKey,i,this.manager.getZone());return this.manager.getZone().runOutsideAngular(()=>Mr().onAndCancel(t,s.domEventName,a,r))}static parseEventName(t){const o=t.toLowerCase().split("."),i=o.shift();if(0===o.length||"keydown"!==i&&"keyup"!==i)return null;const r=e._normalizeKey(o.pop());let s="",a=o.indexOf("code");if(a>-1&&(o.splice(a,1),s="code."),EE.forEach(c=>{const u=o.indexOf(c);u>-1&&(o.splice(u,1),s+=c+".")}),s+=r,0!=o.length||0===r.length)return null;const l={};return l.domEventName=i,l.fullKey=s,l}static matchEventFullKeyCode(t,o){let i=RH[t.key]||t.key,r="";return o.indexOf("code.")>-1&&(i=t.code,r="code."),!(null==i||!i)&&(i=i.toLowerCase()," "===i?i="space":"."===i&&(i="dot"),EE.forEach(s=>{s!==i&&(0,kH[s])(t)&&(r+=s+".")}),r+=i,r===o)}static eventCallback(t,o,i){return r=>{e.matchEventFullKeyCode(r,t)&&i.runGuarded(()=>o(r))}}static _normalizeKey(t){return"esc"===t?"escape":t}static \u0275fac=function(o){return new(o||e)(oe(Bn))};static \u0275prov=X({token:e,factory:e.\u0275fac})}return e})();const HH=yw(d2,"browser",[{provide:C_,useValue:"browser"},{provide:y_,useValue:function LH(){ag.makeCurrent()},multi:!0},{provide:Bn,useFactory:function VH(){return function HS(e){rd=e}(document),document}}]),TE=[{provide:Ml,useClass:class OH{addToWindow(n){Ie.getAngularTestability=(o,i=!0)=>{const r=n.findTestabilityInTree(o,i);if(null==r)throw new T(5103,!1);return r},Ie.getAllAngularTestabilities=()=>n.getAllTestabilities(),Ie.getAllAngularRootElements=()=>n.getAllRootElements(),Ie.frameworkStabilizers||(Ie.frameworkStabilizers=[]),Ie.frameworkStabilizers.push(o=>{const i=Ie.getAllAngularTestabilities();let r=i.length;const s=function(){r--,0==r&&o()};i.forEach(a=>{a.whenStable(s)})})}findTestabilityInTree(n,t,o){return null==t?null:n.getTestability(t)??(o?Mr().isShadowRoot(t)?this.findTestabilityInTree(n,t.host,!0):this.findTestabilityInTree(n,t.parentElement,!0):null)}}},{provide:eb,useClass:$f,deps:[re,zf,Ml]},{provide:$f,useClass:$f,deps:[re,zf,Ml]}],SE=[{provide:vu,useValue:"root"},{provide:di,useFactory:function PH(){return new di}},{provide:eg,useClass:Xh,multi:!0,deps:[Bn]},{provide:eg,useClass:FH,multi:!0,deps:[Bn]},bE,vE,pE,{provide:mf,useExisting:bE},{provide:class qT{},useClass:xH},[]];let BH=(()=>{class e{constructor(){}static \u0275fac=function(o){return new(o||e)};static \u0275mod=Qn({type:e});static \u0275inj=dn({providers:[...SE,...TE],imports:[hE,f2]})}return e})();function no(e){return this instanceof no?(this.v=e,this):new no(e)}function xE(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e=function dg(e){var n="function"==typeof Symbol&&Symbol.iterator,t=n&&e[n],o=0;if(t)return t.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&o>=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(n?"Object is not iterable.":"Symbol.iterator is not defined.")}(e),t={},o("next"),o("throw"),o("return"),t[Symbol.asyncIterator]=function(){return this},t);function o(r){t[r]=e[r]&&function(s){return new Promise(function(a,l){!function i(r,s,a,l){Promise.resolve(l).then(function(c){r({value:c,done:a})},s)}(a,l,(s=e[r](s)).done,s.value)})}}}"function"==typeof SuppressedError&&SuppressedError;const RE=e=>e&&"number"==typeof e.length&&"function"!=typeof e;function kE(e){return ke(e?.then)}function FE(e){return ke(e[Jc])}function LE(e){return Symbol.asyncIterator&&ke(e?.[Symbol.asyncIterator])}function PE(e){return new TypeError(`You provided ${null!==e&&"object"==typeof e?"an invalid object":`'${e}'`} where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`)}const VE=function hB(){return"function"==typeof Symbol&&Symbol.iterator?Symbol.iterator:"@@iterator"}();function HE(e){return ke(e?.[VE])}function BE(e){return function OE(e,n,t){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var i,o=t.apply(e,n||[]),r=[];return i=Object.create(("function"==typeof AsyncIterator?AsyncIterator:Object).prototype),a("next"),a("throw"),a("return",function s(h){return function(p){return Promise.resolve(p).then(h,d)}}),i[Symbol.asyncIterator]=function(){return this},i;function a(h,p){o[h]&&(i[h]=function(b){return new Promise(function(I,N){r.push([h,b,I,N])>1||l(h,b)})},p&&(i[h]=p(i[h])))}function l(h,p){try{!function c(h){h.value instanceof no?Promise.resolve(h.value.v).then(u,d):g(r[0][2],h)}(o[h](p))}catch(b){g(r[0][3],b)}}function u(h){l("next",h)}function d(h){l("throw",h)}function g(h,p){h(p),r.shift(),r.length&&l(r[0][0],r[0][1])}}(this,arguments,function*(){const t=e.getReader();try{for(;;){const{value:o,done:i}=yield no(t.read());if(i)return yield no(void 0);yield yield no(o)}}finally{t.releaseLock()}})}function jE(e){return ke(e?.getReader)}function Ss(e){if(e instanceof ht)return e;if(null!=e){if(FE(e))return function gB(e){return new ht(n=>{const t=e[Jc]();if(ke(t.subscribe))return t.subscribe(n);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}(e);if(RE(e))return function pB(e){return new ht(n=>{for(let t=0;t{e.then(t=>{n.closed||(n.next(t),n.complete())},t=>n.error(t)).then(null,ep)})}(e);if(LE(e))return UE(e);if(HE(e))return function _B(e){return new ht(n=>{for(const t of e)if(n.next(t),n.closed)return;n.complete()})}(e);if(jE(e))return function vB(e){return UE(BE(e))}(e)}throw PE(e)}function UE(e){return new ht(n=>{(function yB(e,n){var t,o,i,r;return function AE(e,n,t,o){return new(t||(t=Promise))(function(r,s){function a(u){try{c(o.next(u))}catch(d){s(d)}}function l(u){try{c(o.throw(u))}catch(d){s(d)}}function c(u){u.done?r(u.value):function i(r){return r instanceof t?r:new t(function(s){s(r)})}(u.value).then(a,l)}c((o=o.apply(e,n||[])).next())})}(this,void 0,void 0,function*(){try{for(t=xE(e);!(o=yield t.next()).done;)if(n.next(o.value),n.closed)return}catch(s){i={error:s}}finally{try{o&&!o.done&&(r=t.return)&&(yield r.call(t))}finally{if(i)throw i.error}}n.complete()})})(e,n).catch(t=>n.error(t))})}function Wo(e,n,t,o=0,i=!1){const r=n.schedule(function(){t(),i?e.add(this.schedule(null,o)):this.unsubscribe()},o);if(e.add(r),!i)return r}function $E(e,n=0){return Mo((t,o)=>{t.subscribe(jn(o,i=>Wo(o,e,()=>o.next(i),n),()=>Wo(o,e,()=>o.complete(),n),i=>Wo(o,e,()=>o.error(i),n)))})}function zE(e,n=0){return Mo((t,o)=>{o.add(e.schedule(()=>t.subscribe(o),n))})}function GE(e,n){if(!e)throw new Error("Iterable cannot be null");return new ht(t=>{Wo(t,n,()=>{const o=e[Symbol.asyncIterator]();Wo(t,n,()=>{o.next().then(i=>{i.done?t.complete():t.next(i.value)})},0,!0)})})}const{isArray:TB}=Array,{getPrototypeOf:SB,prototype:AB,keys:NB}=Object;const{isArray:kB}=Array;function PB(e,n){return e.reduce((t,o,i)=>(t[o]=n[i],t),{})}function VB(...e){const n=function RB(e){return ke(function hg(e){return e[e.length-1]}(e))?e.pop():void 0}(e),{args:t,keys:o}=function OB(e){if(1===e.length){const n=e[0];if(TB(n))return{args:n,keys:null};if(function xB(e){return e&&"object"==typeof e&&SB(e)===AB}(n)){const t=NB(n);return{args:t.map(o=>n[o]),keys:t}}}return{args:e,keys:null}}(e),i=new ht(r=>{const{length:s}=t;if(!s)return void r.complete();const a=new Array(s);let l=s,c=s;for(let u=0;u{d||(d=!0,c--),a[u]=g},()=>l--,void 0,()=>{(!l||!d)&&(c||r.next(o?PB(o,a):a),r.complete())}))}});return n?i.pipe(function LB(e){return $u(n=>function FB(e,n){return kB(n)?e(...n):e(n)}(e,n))}(n)):i}let WE=(()=>{class e{_renderer;_elementRef;onChange=t=>{};onTouched=()=>{};constructor(t,o){this._renderer=t,this._elementRef=o}setProperty(t,o){this._renderer.setProperty(this._elementRef.nativeElement,t,o)}registerOnTouched(t){this.onTouched=t}registerOnChange(t){this.onChange=t}setDisabledState(t){this.setProperty("disabled",t)}static \u0275fac=function(o){return new(o||e)(x(Sn),x(Nt))};static \u0275dir=W({type:e})}return e})(),qo=(()=>{class e extends WE{static \u0275fac=(()=>{let t;return function(i){return(t||(t=ze(e)))(i||e)}})();static \u0275dir=W({type:e,features:[ae]})}return e})();const Kt=new R(""),HB={provide:Kt,useExisting:ge(()=>gg),multi:!0};let gg=(()=>{class e extends qo{writeValue(t){this.setProperty("checked",t)}static \u0275fac=(()=>{let t;return function(i){return(t||(t=ze(e)))(i||e)}})();static \u0275dir=W({type:e,selectors:[["input","type","checkbox","formControlName",""],["input","type","checkbox","formControl",""],["input","type","checkbox","ngModel",""]],hostBindings:function(o,i){1&o&&U("change",function(s){return i.onChange(s.target.checked)})("blur",function(){return i.onTouched()})},standalone:!1,features:[Ee([HB]),ae]})}return e})();const BB={provide:Kt,useExisting:ge(()=>As),multi:!0},UB=new R("");let As=(()=>{class e extends WE{_compositionMode;_composing=!1;constructor(t,o,i){super(t,o),this._compositionMode=i,null==this._compositionMode&&(this._compositionMode=!function jB(){const e=Mr()?Mr().getUserAgent():"";return/android (\d+)/.test(e.toLowerCase())}())}writeValue(t){this.setProperty("value",t??"")}_handleInput(t){(!this._compositionMode||this._compositionMode&&!this._composing)&&this.onChange(t)}_compositionStart(){this._composing=!0}_compositionEnd(t){this._composing=!1,this._compositionMode&&this.onChange(t)}static \u0275fac=function(o){return new(o||e)(x(Sn),x(Nt),x(UB,8))};static \u0275dir=W({type:e,selectors:[["input","formControlName","",3,"type","checkbox"],["textarea","formControlName",""],["input","formControl","",3,"type","checkbox"],["textarea","formControl",""],["input","ngModel","",3,"type","checkbox"],["textarea","ngModel",""],["","ngDefaultControl",""]],hostBindings:function(o,i){1&o&&U("input",function(s){return i._handleInput(s.target.value)})("blur",function(){return i.onTouched()})("compositionstart",function(){return i._compositionStart()})("compositionend",function(s){return i._compositionEnd(s.target.value)})},standalone:!1,features:[Ee([BB]),ae]})}return e})();const ot=new R(""),oo=new R("");function tM(e){return null!=e}function nM(e){return Il(e)?function IB(e,n){return n?function MB(e,n){if(null!=e){if(FE(e))return function CB(e,n){return Ss(e).pipe(zE(n),$E(n))}(e,n);if(RE(e))return function DB(e,n){return new ht(t=>{let o=0;return n.schedule(function(){o===e.length?t.complete():(t.next(e[o++]),t.closed||this.schedule())})})}(e,n);if(kE(e))return function bB(e,n){return Ss(e).pipe(zE(n),$E(n))}(e,n);if(LE(e))return GE(e,n);if(HE(e))return function wB(e,n){return new ht(t=>{let o;return Wo(t,n,()=>{o=e[VE](),Wo(t,n,()=>{let i,r;try{({value:i,done:r}=o.next())}catch(s){return void t.error(s)}r?t.complete():t.next(i)},0,!0)}),()=>ke(o?.return)&&o.return()})}(e,n);if(jE(e))return function EB(e,n){return GE(BE(e),n)}(e,n)}throw PE(e)}(e,n):Ss(e)}(e):e}function oM(e){let n={};return e.forEach(t=>{n=null!=t?{...n,...t}:n}),0===Object.keys(n).length?null:n}function iM(e,n){return n.map(t=>t(e))}function rM(e){return e.map(n=>function zB(e){return!e.validate}(n)?n:t=>n.validate(t))}function _g(e){return null!=e?function sM(e){if(!e)return null;const n=e.filter(tM);return 0==n.length?null:function(t){return oM(iM(t,n))}}(rM(e)):null}function vg(e){return null!=e?function aM(e){if(!e)return null;const n=e.filter(tM);return 0==n.length?null:function(t){return VB(iM(t,n).map(nM)).pipe($u(oM))}}(rM(e)):null}function lM(e,n){return null===e?[n]:Array.isArray(e)?[...e,n]:[e,n]}function yg(e){return e?Array.isArray(e)?e:[e]:[]}function _c(e,n){return Array.isArray(e)?e.includes(n):e===n}function dM(e,n){const t=yg(n);return yg(e).forEach(i=>{_c(t,i)||t.push(i)}),t}function fM(e,n){return yg(n).filter(t=>!_c(e,t))}class hM{get value(){return this.control?this.control.value:null}get valid(){return this.control?this.control.valid:null}get invalid(){return this.control?this.control.invalid:null}get pending(){return this.control?this.control.pending:null}get disabled(){return this.control?this.control.disabled:null}get enabled(){return this.control?this.control.enabled:null}get errors(){return this.control?this.control.errors:null}get pristine(){return this.control?this.control.pristine:null}get dirty(){return this.control?this.control.dirty:null}get touched(){return this.control?this.control.touched:null}get status(){return this.control?this.control.status:null}get untouched(){return this.control?this.control.untouched:null}get statusChanges(){return this.control?this.control.statusChanges:null}get valueChanges(){return this.control?this.control.valueChanges:null}get path(){return null}_composedValidatorFn;_composedAsyncValidatorFn;_rawValidators=[];_rawAsyncValidators=[];_setValidators(n){this._rawValidators=n||[],this._composedValidatorFn=_g(this._rawValidators)}_setAsyncValidators(n){this._rawAsyncValidators=n||[],this._composedAsyncValidatorFn=vg(this._rawAsyncValidators)}get validator(){return this._composedValidatorFn||null}get asyncValidator(){return this._composedAsyncValidatorFn||null}_onDestroyCallbacks=[];_registerOnDestroy(n){this._onDestroyCallbacks.push(n)}_invokeOnDestroyCallbacks(){this._onDestroyCallbacks.forEach(n=>n()),this._onDestroyCallbacks=[]}reset(n=void 0){this.control&&this.control.reset(n)}hasError(n,t){return!!this.control&&this.control.hasError(n,t)}getError(n,t){return this.control?this.control.getError(n,t):null}}class ft extends hM{name;get formDirective(){return null}get path(){return null}}class io extends hM{_parent=null;name=null;valueAccessor=null}class gM{_cd;constructor(n){this._cd=n}get isTouched(){return this._cd?.control?._touched?.(),!!this._cd?.control?.touched}get isUntouched(){return!!this._cd?.control?.untouched}get isPristine(){return this._cd?.control?._pristine?.(),!!this._cd?.control?.pristine}get isDirty(){return!!this._cd?.control?.dirty}get isValid(){return this._cd?.control?._status?.(),!!this._cd?.control?.valid}get isInvalid(){return!!this._cd?.control?.invalid}get isPending(){return!!this._cd?.control?.pending}get isSubmitted(){return this._cd?._submitted?.(),!!this._cd?.submitted}}let vc=(()=>{class e extends gM{constructor(t){super(t)}static \u0275fac=function(o){return new(o||e)(x(io,2))};static \u0275dir=W({type:e,selectors:[["","formControlName",""],["","ngModel",""],["","formControl",""]],hostVars:14,hostBindings:function(o,i){2&o&&An("ng-untouched",i.isUntouched)("ng-touched",i.isTouched)("ng-pristine",i.isPristine)("ng-dirty",i.isDirty)("ng-valid",i.isValid)("ng-invalid",i.isInvalid)("ng-pending",i.isPending)},standalone:!1,features:[ae]})}return e})();const Ns="VALID",Cc="INVALID",er="PENDING",Os="DISABLED";class tr{}class mM extends tr{value;source;constructor(n,t){super(),this.value=n,this.source=t}}class Dg extends tr{pristine;source;constructor(n,t){super(),this.pristine=n,this.source=t}}class wg extends tr{touched;source;constructor(n,t){super(),this.touched=n,this.source=t}}class bc extends tr{status;source;constructor(n,t){super(),this.status=n,this.source=t}}class Eg extends tr{source;constructor(n){super(),this.source=n}}function Dc(e){return null!=e&&!Array.isArray(e)&&"object"==typeof e}class Tg{_pendingDirty=!1;_hasOwnPendingAsyncValidator=null;_pendingTouched=!1;_onCollectionChange=()=>{};_updateOn;_parent=null;_asyncValidationSubscription;_composedValidatorFn;_composedAsyncValidatorFn;_rawValidators;_rawAsyncValidators;value;constructor(n,t){this._assignValidators(n),this._assignAsyncValidators(t)}get validator(){return this._composedValidatorFn}set validator(n){this._rawValidators=this._composedValidatorFn=n}get asyncValidator(){return this._composedAsyncValidatorFn}set asyncValidator(n){this._rawAsyncValidators=this._composedAsyncValidatorFn=n}get parent(){return this._parent}get status(){return Pe(this.statusReactive)}set status(n){Pe(()=>this.statusReactive.set(n))}_status=Zt(()=>this.statusReactive());statusReactive=wo(void 0);get valid(){return this.status===Ns}get invalid(){return this.status===Cc}get pending(){return this.status==er}get disabled(){return this.status===Os}get enabled(){return this.status!==Os}errors;get pristine(){return Pe(this.pristineReactive)}set pristine(n){Pe(()=>this.pristineReactive.set(n))}_pristine=Zt(()=>this.pristineReactive());pristineReactive=wo(!0);get dirty(){return!this.pristine}get touched(){return Pe(this.touchedReactive)}set touched(n){Pe(()=>this.touchedReactive.set(n))}_touched=Zt(()=>this.touchedReactive());touchedReactive=wo(!1);get untouched(){return!this.touched}_events=new Xt;events=this._events.asObservable();valueChanges;statusChanges;get updateOn(){return this._updateOn?this._updateOn:this.parent?this.parent.updateOn:"change"}setValidators(n){this._assignValidators(n)}setAsyncValidators(n){this._assignAsyncValidators(n)}addValidators(n){this.setValidators(dM(n,this._rawValidators))}addAsyncValidators(n){this.setAsyncValidators(dM(n,this._rawAsyncValidators))}removeValidators(n){this.setValidators(fM(n,this._rawValidators))}removeAsyncValidators(n){this.setAsyncValidators(fM(n,this._rawAsyncValidators))}hasValidator(n){return _c(this._rawValidators,n)}hasAsyncValidator(n){return _c(this._rawAsyncValidators,n)}clearValidators(){this.validator=null}clearAsyncValidators(){this.asyncValidator=null}markAsTouched(n={}){const t=!1===this.touched;this.touched=!0;const o=n.sourceControl??this;this._parent&&!n.onlySelf&&this._parent.markAsTouched({...n,sourceControl:o}),t&&!1!==n.emitEvent&&this._events.next(new wg(!0,o))}markAllAsDirty(n={}){this.markAsDirty({onlySelf:!0,emitEvent:n.emitEvent,sourceControl:this}),this._forEachChild(t=>t.markAllAsDirty(n))}markAllAsTouched(n={}){this.markAsTouched({onlySelf:!0,emitEvent:n.emitEvent,sourceControl:this}),this._forEachChild(t=>t.markAllAsTouched(n))}markAsUntouched(n={}){const t=!0===this.touched;this.touched=!1,this._pendingTouched=!1;const o=n.sourceControl??this;this._forEachChild(i=>{i.markAsUntouched({onlySelf:!0,emitEvent:n.emitEvent,sourceControl:o})}),this._parent&&!n.onlySelf&&this._parent._updateTouched(n,o),t&&!1!==n.emitEvent&&this._events.next(new wg(!1,o))}markAsDirty(n={}){const t=!0===this.pristine;this.pristine=!1;const o=n.sourceControl??this;this._parent&&!n.onlySelf&&this._parent.markAsDirty({...n,sourceControl:o}),t&&!1!==n.emitEvent&&this._events.next(new Dg(!1,o))}markAsPristine(n={}){const t=!1===this.pristine;this.pristine=!0,this._pendingDirty=!1;const o=n.sourceControl??this;this._forEachChild(i=>{i.markAsPristine({onlySelf:!0,emitEvent:n.emitEvent})}),this._parent&&!n.onlySelf&&this._parent._updatePristine(n,o),t&&!1!==n.emitEvent&&this._events.next(new Dg(!0,o))}markAsPending(n={}){this.status=er;const t=n.sourceControl??this;!1!==n.emitEvent&&(this._events.next(new bc(this.status,t)),this.statusChanges.emit(this.status)),this._parent&&!n.onlySelf&&this._parent.markAsPending({...n,sourceControl:t})}disable(n={}){const t=this._parentMarkedDirty(n.onlySelf);this.status=Os,this.errors=null,this._forEachChild(i=>{i.disable({...n,onlySelf:!0})}),this._updateValue();const o=n.sourceControl??this;!1!==n.emitEvent&&(this._events.next(new mM(this.value,o)),this._events.next(new bc(this.status,o)),this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._updateAncestors({...n,skipPristineCheck:t},this),this._onDisabledChange.forEach(i=>i(!0))}enable(n={}){const t=this._parentMarkedDirty(n.onlySelf);this.status=Ns,this._forEachChild(o=>{o.enable({...n,onlySelf:!0})}),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent}),this._updateAncestors({...n,skipPristineCheck:t},this),this._onDisabledChange.forEach(o=>o(!1))}_updateAncestors(n,t){this._parent&&!n.onlySelf&&(this._parent.updateValueAndValidity(n),n.skipPristineCheck||this._parent._updatePristine({},t),this._parent._updateTouched({},t))}setParent(n){this._parent=n}getRawValue(){return this.value}updateValueAndValidity(n={}){if(this._setInitialStatus(),this._updateValue(),this.enabled){const o=this._cancelExistingSubscription();this.errors=this._runValidator(),this.status=this._calculateStatus(),(this.status===Ns||this.status===er)&&this._runAsyncValidator(o,n.emitEvent)}const t=n.sourceControl??this;!1!==n.emitEvent&&(this._events.next(new mM(this.value,t)),this._events.next(new bc(this.status,t)),this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._parent&&!n.onlySelf&&this._parent.updateValueAndValidity({...n,sourceControl:t})}_updateTreeValidity(n={emitEvent:!0}){this._forEachChild(t=>t._updateTreeValidity(n)),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent})}_setInitialStatus(){this.status=this._allControlsDisabled()?Os:Ns}_runValidator(){return this.validator?this.validator(this):null}_runAsyncValidator(n,t){if(this.asyncValidator){this.status=er,this._hasOwnPendingAsyncValidator={emitEvent:!1!==t,shouldHaveEmitted:!1!==n};const o=nM(this.asyncValidator(this));this._asyncValidationSubscription=o.subscribe(i=>{this._hasOwnPendingAsyncValidator=null,this.setErrors(i,{emitEvent:t,shouldHaveEmitted:n})})}}_cancelExistingSubscription(){if(this._asyncValidationSubscription){this._asyncValidationSubscription.unsubscribe();const n=(this._hasOwnPendingAsyncValidator?.emitEvent||this._hasOwnPendingAsyncValidator?.shouldHaveEmitted)??!1;return this._hasOwnPendingAsyncValidator=null,n}return!1}setErrors(n,t={}){this.errors=n,this._updateControlsErrors(!1!==t.emitEvent,this,t.shouldHaveEmitted)}get(n){let t=n;return null==t||(Array.isArray(t)||(t=t.split(".")),0===t.length)?null:t.reduce((o,i)=>o&&o._find(i),this)}getError(n,t){const o=t?this.get(t):this;return o&&o.errors?o.errors[n]:null}hasError(n,t){return!!this.getError(n,t)}get root(){let n=this;for(;n._parent;)n=n._parent;return n}_updateControlsErrors(n,t,o){this.status=this._calculateStatus(),n&&this.statusChanges.emit(this.status),(n||o)&&this._events.next(new bc(this.status,t)),this._parent&&this._parent._updateControlsErrors(n,t,o)}_initObservables(){this.valueChanges=new ve,this.statusChanges=new ve}_calculateStatus(){return this._allControlsDisabled()?Os:this.errors?Cc:this._hasOwnPendingAsyncValidator||this._anyControlsHaveStatus(er)?er:this._anyControlsHaveStatus(Cc)?Cc:Ns}_anyControlsHaveStatus(n){return this._anyControls(t=>t.status===n)}_anyControlsDirty(){return this._anyControls(n=>n.dirty)}_anyControlsTouched(){return this._anyControls(n=>n.touched)}_updatePristine(n,t){const o=!this._anyControlsDirty(),i=this.pristine!==o;this.pristine=o,this._parent&&!n.onlySelf&&this._parent._updatePristine(n,t),i&&this._events.next(new Dg(this.pristine,t))}_updateTouched(n={},t){this.touched=this._anyControlsTouched(),this._events.next(new wg(this.touched,t)),this._parent&&!n.onlySelf&&this._parent._updateTouched(n,t)}_onDisabledChange=[];_registerOnCollectionChange(n){this._onCollectionChange=n}_setUpdateStrategy(n){Dc(n)&&null!=n.updateOn&&(this._updateOn=n.updateOn)}_parentMarkedDirty(n){return!n&&!(!this._parent||!this._parent.dirty)&&!this._parent._anyControlsDirty()}_find(n){return null}_assignValidators(n){this._rawValidators=Array.isArray(n)?n.slice():n,this._composedValidatorFn=function JB(e){return Array.isArray(e)?_g(e):e||null}(this._rawValidators)}_assignAsyncValidators(n){this._rawAsyncValidators=Array.isArray(n)?n.slice():n,this._composedAsyncValidatorFn=function XB(e){return Array.isArray(e)?vg(e):e||null}(this._rawAsyncValidators)}}const nr=new R("",{providedIn:"root",factory:()=>wc}),wc="always";function xs(e,n,t=wc){(function Ag(e,n){const t=function cM(e){return e._rawValidators}(e);null!==n.validator?e.setValidators(lM(t,n.validator)):"function"==typeof t&&e.setValidators([t]);const o=function uM(e){return e._rawAsyncValidators}(e);null!==n.asyncValidator?e.setAsyncValidators(lM(o,n.asyncValidator)):"function"==typeof o&&e.setAsyncValidators([o]);const i=()=>e.updateValueAndValidity();Ic(n._rawValidators,i),Ic(n._rawAsyncValidators,i)})(e,n),n.valueAccessor.writeValue(e.value),(e.disabled||"always"===t)&&n.valueAccessor.setDisabledState?.(e.disabled),function nj(e,n){n.valueAccessor.registerOnChange(t=>{e._pendingValue=t,e._pendingChange=!0,e._pendingDirty=!0,"change"===e.updateOn&&CM(e,n)})}(e,n),function ij(e,n){const t=(o,i)=>{n.valueAccessor.writeValue(o),i&&n.viewToModelUpdate(o)};e.registerOnChange(t),n._registerOnDestroy(()=>{e._unregisterOnChange(t)})}(e,n),function oj(e,n){n.valueAccessor.registerOnTouched(()=>{e._pendingTouched=!0,"blur"===e.updateOn&&e._pendingChange&&CM(e,n),"submit"!==e.updateOn&&e.markAsTouched()})}(e,n),function tj(e,n){if(n.valueAccessor.setDisabledState){const t=o=>{n.valueAccessor.setDisabledState(o)};e.registerOnDisabledChange(t),n._registerOnDestroy(()=>{e._unregisterOnDisabledChange(t)})}}(e,n)}function Ic(e,n){e.forEach(t=>{t.registerOnValidatorChange&&t.registerOnValidatorChange(n)})}function CM(e,n){e._pendingDirty&&e.markAsDirty(),e.setValue(e._pendingValue,{emitModelToViewChange:!1}),n.viewToModelUpdate(e._pendingValue),e._pendingChange=!1}function wM(e,n){const t=e.indexOf(n);t>-1&&e.splice(t,1)}function EM(e){return"object"==typeof e&&null!==e&&2===Object.keys(e).length&&"value"in e&&"disabled"in e}Promise.resolve();const MM=class extends Tg{defaultValue=null;_onChange=[];_pendingValue;_pendingChange=!1;constructor(n=null,t,o){super(function Mg(e){return(Dc(e)?e.validators:e)||null}(t),function Ig(e,n){return(Dc(n)?n.asyncValidators:e)||null}(o,t)),this._applyFormState(n),this._setUpdateStrategy(t),this._initObservables(),this.updateValueAndValidity({onlySelf:!0,emitEvent:!!this.asyncValidator}),Dc(t)&&(t.nonNullable||t.initialValueIsDefault)&&(this.defaultValue=EM(n)?n.value:n)}setValue(n,t={}){this.value=this._pendingValue=n,this._onChange.length&&!1!==t.emitModelToViewChange&&this._onChange.forEach(o=>o(this.value,!1!==t.emitViewToModelChange)),this.updateValueAndValidity(t)}patchValue(n,t={}){this.setValue(n,t)}reset(n=this.defaultValue,t={}){this._applyFormState(n),this.markAsPristine(t),this.markAsUntouched(t),this.setValue(this.value,t),this._pendingChange=!1,!1!==t?.emitEvent&&this._events.next(new Eg(this))}_updateValue(){}_anyControls(n){return!1}_allControlsDisabled(){return this.disabled}registerOnChange(n){this._onChange.push(n)}_unregisterOnChange(n){wM(this._onChange,n)}registerOnDisabledChange(n){this._onDisabledChange.push(n)}_unregisterOnDisabledChange(n){wM(this._onDisabledChange,n)}_forEachChild(n){}_syncPendingControls(){return!("submit"!==this.updateOn||(this._pendingDirty&&this.markAsDirty(),this._pendingTouched&&this.markAsTouched(),!this._pendingChange)||(this.setValue(this._pendingValue,{onlySelf:!0,emitModelToViewChange:!1}),0))}_applyFormState(n){EM(n)?(this.value=this._pendingValue=n.value,n.disabled?this.disable({onlySelf:!0,emitEvent:!1}):this.enable({onlySelf:!0,emitEvent:!1})):this.value=this._pendingValue=n}},gj={provide:io,useExisting:ge(()=>ks)},IM=Promise.resolve();let ks=(()=>{class e extends io{_changeDetectorRef;callSetDisabledState;control=new MM;static ngAcceptInputType_isDisabled;_registered=!1;viewModel;name="";isDisabled;model;options;update=new ve;constructor(t,o,i,r,s,a){super(),this._changeDetectorRef=s,this.callSetDisabledState=a,this._parent=t,this._setValidators(o),this._setAsyncValidators(i),this.valueAccessor=function xg(e,n){if(!n)return null;let t,o,i;return Array.isArray(n),n.forEach(r=>{r.constructor===As?t=r:function aj(e){return Object.getPrototypeOf(e.constructor)===qo}(r)?o=r:i=r}),i||o||t||null}(0,r)}ngOnChanges(t){if(this._checkForErrors(),!this._registered||"name"in t){if(this._registered&&(this._checkName(),this.formDirective)){const o=t.name.previousValue;this.formDirective.removeControl({name:o,path:this._getPath(o)})}this._setUpControl()}"isDisabled"in t&&this._updateDisabled(t),function Og(e,n){if(!e.hasOwnProperty("model"))return!1;const t=e.model;return!!t.isFirstChange()||!Object.is(n,t.currentValue)}(t,this.viewModel)&&(this._updateValue(this.model),this.viewModel=this.model)}ngOnDestroy(){this.formDirective&&this.formDirective.removeControl(this)}get path(){return this._getPath(this.name)}get formDirective(){return this._parent?this._parent.formDirective:null}viewToModelUpdate(t){this.viewModel=t,this.update.emit(t)}_setUpControl(){this._setUpdateStrategy(),this._isStandalone()?this._setUpStandalone():this.formDirective.addControl(this),this._registered=!0}_setUpdateStrategy(){this.options&&null!=this.options.updateOn&&(this.control._updateOn=this.options.updateOn)}_isStandalone(){return!this._parent||!(!this.options||!this.options.standalone)}_setUpStandalone(){xs(this.control,this,this.callSetDisabledState),this.control.updateValueAndValidity({emitEvent:!1})}_checkForErrors(){this._checkName()}_checkName(){this.options&&this.options.name&&(this.name=this.options.name),this._isStandalone()}_updateValue(t){IM.then(()=>{this.control.setValue(t,{emitViewToModelChange:!1}),this._changeDetectorRef?.markForCheck()})}_updateDisabled(t){const o=t.isDisabled.currentValue,i=0!==o&&function Lh(e){return"boolean"==typeof e?e:null!=e&&"false"!==e}(o);IM.then(()=>{i&&!this.control.disabled?this.control.disable():!i&&this.control.disabled&&this.control.enable(),this._changeDetectorRef?.markForCheck()})}_getPath(t){return this._parent?function Ec(e,n){return[...n.path,e]}(t,this._parent):[t]}static \u0275fac=function(o){return new(o||e)(x(ft,9),x(ot,10),x(oo,10),x(Kt,10),x(Es,8),x(nr,8))};static \u0275dir=W({type:e,selectors:[["","ngModel","",3,"formControlName","",3,"formControl",""]],inputs:{name:"name",isDisabled:[0,"disabled","isDisabled"],model:[0,"ngModel","model"],options:[0,"ngModelOptions","options"]},outputs:{update:"ngModelChange"},exportAs:["ngModel"],standalone:!1,features:[Ee([gj]),ae,En]})}return e})();const yj={provide:Kt,useExisting:ge(()=>Rg),multi:!0};let Rg=(()=>{class e extends qo{writeValue(t){this.setProperty("value",parseFloat(t))}registerOnChange(t){this.onChange=o=>{t(""==o?null:parseFloat(o))}}static \u0275fac=(()=>{let t;return function(i){return(t||(t=ze(e)))(i||e)}})();static \u0275dir=W({type:e,selectors:[["input","type","range","formControlName",""],["input","type","range","formControl",""],["input","type","range","ngModel",""]],hostBindings:function(o,i){1&o&&U("change",function(s){return i.onChange(s.target.value)})("input",function(s){return i.onChange(s.target.value)})("blur",function(){return i.onTouched()})},standalone:!1,features:[Ee([yj]),ae]})}return e})();const Mj={provide:Kt,useExisting:ge(()=>Ls),multi:!0};function RM(e,n){return null==e?`${n}`:(n&&"object"==typeof n&&(n="Object"),`${e}: ${n}`.slice(0,50))}let Ls=(()=>{class e extends qo{value;_optionMap=new Map;_idCounter=0;set compareWith(t){this._compareWith=t}_compareWith=Object.is;appRefInjector=F(Jn).injector;destroyRef=F(bn);cdr=F(Es);_queuedWrite=!1;_writeValueAfterRender(){this._queuedWrite||this.appRefInjector.destroyed||(this._queuedWrite=!0,Gd({write:()=>{this.destroyRef.destroyed||(this._queuedWrite=!1,this.writeValue(this.value))}},{injector:this.appRefInjector}))}writeValue(t){this.cdr.markForCheck(),this.value=t;const i=RM(this._getOptionId(t),t);this.setProperty("value",i)}registerOnChange(t){this.onChange=o=>{this.value=this._getOptionValue(o),t(this.value)}}_registerOption(){return(this._idCounter++).toString()}_getOptionId(t){for(const o of this._optionMap.keys())if(this._compareWith(this._optionMap.get(o),t))return o;return null}_getOptionValue(t){const o=function Ij(e){return e.split(":")[0]}(t);return this._optionMap.has(o)?this._optionMap.get(o):t}static \u0275fac=(()=>{let t;return function(i){return(t||(t=ze(e)))(i||e)}})();static \u0275dir=W({type:e,selectors:[["select","formControlName","",3,"multiple",""],["select","formControl","",3,"multiple",""],["select","ngModel","",3,"multiple",""]],hostBindings:function(o,i){1&o&&U("change",function(s){return i.onChange(s.target.value)})("blur",function(){return i.onTouched()})},inputs:{compareWith:"compareWith"},standalone:!1,features:[Ee([Mj]),ae]})}return e})(),kg=(()=>{class e{_element;_renderer;_select;id;constructor(t,o,i){this._element=t,this._renderer=o,this._select=i,this._select&&(this.id=this._select._registerOption())}set ngValue(t){null!=this._select&&(this._select._optionMap.set(this.id,t),this._setElementValue(RM(this.id,t)),this._select._writeValueAfterRender())}set value(t){this._setElementValue(t),this._select&&this._select._writeValueAfterRender()}_setElementValue(t){this._renderer.setProperty(this._element.nativeElement,"value",t)}ngOnDestroy(){this._select&&(this._select._optionMap.delete(this.id),this._select._writeValueAfterRender())}static \u0275fac=function(o){return new(o||e)(x(Nt),x(Sn),x(Ls,9))};static \u0275dir=W({type:e,selectors:[["option"]],inputs:{ngValue:"ngValue",value:"value"},standalone:!1})}return e})();const Tj={provide:Kt,useExisting:ge(()=>Fg),multi:!0};function kM(e,n){return null==e?`${n}`:("string"==typeof n&&(n=`'${n}'`),n&&"object"==typeof n&&(n="Object"),`${e}: ${n}`.slice(0,50))}let Fg=(()=>{class e extends qo{value;_optionMap=new Map;_idCounter=0;set compareWith(t){this._compareWith=t}_compareWith=Object.is;writeValue(t){let o;if(this.value=t,Array.isArray(t)){const i=t.map(r=>this._getOptionId(r));o=(r,s)=>{r._setSelected(i.indexOf(s.toString())>-1)}}else o=(i,r)=>{i._setSelected(!1)};this._optionMap.forEach(o)}registerOnChange(t){this.onChange=o=>{const i=[],r=o.selectedOptions;if(void 0!==r){const s=r;for(let a=0;a{let t;return function(i){return(t||(t=ze(e)))(i||e)}})();static \u0275dir=W({type:e,selectors:[["select","multiple","","formControlName",""],["select","multiple","","formControl",""],["select","multiple","","ngModel",""]],hostBindings:function(o,i){1&o&&U("change",function(s){return i.onChange(s.target)})("blur",function(){return i.onTouched()})},inputs:{compareWith:"compareWith"},standalone:!1,features:[Ee([Tj]),ae]})}return e})(),Lg=(()=>{class e{_element;_renderer;_select;id;_value;constructor(t,o,i){this._element=t,this._renderer=o,this._select=i,this._select&&(this.id=this._select._registerOption(this))}set ngValue(t){null!=this._select&&(this._value=t,this._setElementValue(kM(this.id,t)),this._select.writeValue(this._select.value))}set value(t){this._select?(this._value=t,this._setElementValue(kM(this.id,t)),this._select.writeValue(this._select.value)):this._setElementValue(t)}_setElementValue(t){this._renderer.setProperty(this._element.nativeElement,"value",t)}_setSelected(t){this._renderer.setProperty(this._element.nativeElement,"selected",t)}ngOnDestroy(){this._select&&(this._select._optionMap.delete(this.id),this._select.writeValue(this._select.value))}static \u0275fac=function(o){return new(o||e)(x(Nt),x(Sn),x(Fg,9))};static \u0275dir=W({type:e,selectors:[["option"]],inputs:{ngValue:"ngValue",value:"value"},standalone:!1})}return e})(),Pj=(()=>{class e{static \u0275fac=function(o){return new(o||e)};static \u0275mod=Qn({type:e});static \u0275inj=dn({})}return e})(),Hj=(()=>{class e{static withConfig(t){return{ngModule:e,providers:[{provide:nr,useValue:t.callSetDisabledState??wc}]}}static \u0275fac=function(o){return new(o||e)};static \u0275mod=Qn({type:e});static \u0275inj=dn({imports:[Pj]})}return e})();class Bj extends It{constructor(n,t){super()}schedule(n,t=0){return this}}const Rc={setInterval(e,n,...t){const{delegate:o}=Rc;return o?.setInterval?o.setInterval(e,n,...t):setInterval(e,n,...t)},clearInterval(e){const{delegate:n}=Rc;return(n?.clearInterval||clearInterval)(e)},delegate:void 0},zM={now:()=>(zM.delegate||Date).now(),delegate:void 0};class Ps{constructor(n,t=Ps.now){this.schedulerActionCtor=n,this.now=t}schedule(n,t=0,o){return new this.schedulerActionCtor(this,n).schedule(o,t)}}Ps.now=zM.now;const GM=new class Uj extends Ps{constructor(n,t=Ps.now){super(n,t),this.actions=[],this._active=!1}flush(n){const{actions:t}=this;if(this._active)return void t.push(n);let o;this._active=!0;do{if(o=n.execute(n.state,n.delay))break}while(n=t.shift());if(this._active=!1,o){for(;n=t.shift();)n.unsubscribe();throw o}}}(class jj extends Bj{constructor(n,t){super(n,t),this.scheduler=n,this.work=t,this.pending=!1}schedule(n,t=0){var o;if(this.closed)return this;this.state=n;const i=this.id,r=this.scheduler;return null!=i&&(this.id=this.recycleAsyncId(r,i,t)),this.pending=!0,this.delay=t,this.id=null!==(o=this.id)&&void 0!==o?o:this.requestAsyncId(r,this.id,t),this}requestAsyncId(n,t,o=0){return Rc.setInterval(n.flush.bind(n,this),o)}recycleAsyncId(n,t,o=0){if(null!=o&&this.delay===o&&!1===this.pending)return t;null!=t&&Rc.clearInterval(t)}execute(n,t){if(this.closed)return new Error("executing a cancelled action");this.pending=!1;const o=this._execute(n,t);if(o)return o;!1===this.pending&&null!=this.id&&(this.id=this.recycleAsyncId(this.scheduler,this.id,null))}_execute(n,t){let i,o=!1;try{this.work(n)}catch(r){o=!0,i=r||new Error("Scheduled action threw falsy error")}if(o)return this.unsubscribe(),i}unsubscribe(){if(!this.closed){const{id:n,scheduler:t}=this,{actions:o}=t;this.work=this.state=this.scheduler=null,this.pending=!1,Us(o,this),null!=n&&(this.id=this.recycleAsyncId(t,n,null)),this.delay=null,super.unsubscribe()}}}),$j=GM;function WM(e,n=GM,t){const o=function qj(e=0,n,t=$j){let o=-1;return null!=n&&(function Gj(e){return e&&ke(e.schedule)}(n)?t=n:o=n),new ht(i=>{let r=function Wj(e){return e instanceof Date&&!isNaN(e)}(e)?+e-t.now():e;r<0&&(r=0);let s=0;return t.schedule(function(){i.closed||(i.next(s++),0<=o?this.schedule(void 0,o):i.complete())},r)})}(e,n);return function zj(e,n){return Mo((t,o)=>{const{leading:i=!0,trailing:r=!1}=n??{};let s=!1,a=null,l=null,c=!1;const u=()=>{l?.unsubscribe(),l=null,r&&(h(),c&&o.complete())},d=()=>{l=null,c&&o.complete()},g=p=>l=Ss(e(p)).subscribe(jn(o,u,d)),h=()=>{if(s){s=!1;const p=a;a=null,o.next(p),!c&&g(p)}};t.subscribe(jn(o,p=>{s=!0,a=p,(!l||l.closed)&&(i?h():g(p))},()=>{c=!0,(!(r&&s&&l)||l.closed)&&o.complete()}))})}(()=>o,t)}function qM(e,n,t){const o=ke(e)||n||t?{next:e,error:n,complete:t}:e;return o?Mo((i,r)=>{var s;null===(s=o.subscribe)||void 0===s||s.call(o);let a=!0;i.subscribe(jn(r,l=>{var c;null===(c=o.next)||void 0===c||c.call(o,l),r.next(l)},()=>{var l;a=!1,null===(l=o.complete)||void 0===l||l.call(o),r.complete()},l=>{var c;a=!1,null===(c=o.error)||void 0===c||c.call(o,l),r.error(l)},()=>{var l,c;a&&(null===(l=o.unsubscribe)||void 0===l||l.call(o)),null===(c=o.finalize)||void 0===c||c.call(o)}))}):Xc}function ZM(e,n=Xc){return e=e??Zj,Mo((t,o)=>{let i,r=!0;t.subscribe(jn(o,s=>{const a=n(s);(r||!e(i,a))&&(r=!1,i=a,o.next(s))}))})}function Zj(e,n){return e===n}var Rt=typeof window<"u"?window:{screen:{},navigator:{}},or=(Rt.matchMedia||function(){return{matches:!1}}).bind(Rt),YM=!1,QM=function(){};Rt.addEventListener&&Rt.addEventListener("p",QM,{get passive(){return YM=!0}}),Rt.removeEventListener&&Rt.removeEventListener("p",QM,!1);var KM=YM,Vg="ontouchstart"in Rt,XM=(Vg||"TouchEvent"in Rt&&or("(any-pointer: coarse)"),Rt.navigator.userAgent||"");or("(pointer: coarse)").matches&&/iPad|Macintosh/.test(XM)&&Math.min(Rt.screen.width||0,Rt.screen.height||0);(or("(pointer: coarse)").matches||!or("(pointer: fine)").matches&&Vg)&&/Windows.*Firefox/.test(XM),or("(any-pointer: fine)").matches||or("(any-hover: hover)");const tU=(e,n,t)=>({tooltip:e,placement:n,content:t});function nU(e,n){}function oU(e,n){1&e&&_l(0,nU,0,0,"ng-template")}function iU(e,n){if(1&e&&_l(0,oU,1,0,null,1),2&e){const t=m();A("ngTemplateOutlet",t.template)("ngTemplateOutletContext",Ne(2,tU,t.tooltip,t.placement,t.content))}}function rU(e,n){if(1&e&&(v(0,"div",0),D(1),_()),2&e){const t=m();ct("title",t.tooltip)("data-tooltip-placement",t.placement),f(),P(" ",t.content," ")}}const sU=["tooltipTemplate"],aU=["leftOuterSelectionBar"],lU=["rightOuterSelectionBar"],cU=["fullBar"],uU=["selectionBar"],dU=["minHandle"],fU=["maxHandle"],hU=["floorLabel"],gU=["ceilLabel"],pU=["minHandleLabel"],mU=["maxHandleLabel"],_U=["combinedLabel"],vU=["ticksElement"],yU=e=>({"ngx-slider-selected":e});function CU(e,n){if(1&e&&O(0,"ngx-slider-tooltip-wrapper",28),2&e){const t=m().$implicit;A("template",m().tooltipTemplate)("tooltip",t.valueTooltip)("placement",t.valueTooltipPlacement)("content",t.value)}}function bU(e,n){1&e&&O(0,"span",29),2&e&&A("innerText",m().$implicit.legend)}function DU(e,n){1&e&&O(0,"span",30),2&e&&A("innerHTML",m().$implicit.legend,pv)}function wU(e,n){if(1&e&&(v(0,"span",26),O(1,"ngx-slider-tooltip-wrapper",27),y(2,CU,1,4,"ngx-slider-tooltip-wrapper",28),y(3,bU,1,1,"span",29),y(4,DU,1,1,"span",30),_()),2&e){const t=n.$implicit,o=m();A("ngClass",Zi(8,yU,t.selected))("ngStyle",t.style),f(),A("template",o.tooltipTemplate)("tooltip",t.tooltip)("placement",t.tooltipPlacement),f(),C(null!=t.value?2:-1),f(),C(null!=t.legend&&!1===o.allowUnsafeHtmlInSlider?3:-1),f(),C(null==t.legend||null!=o.allowUnsafeHtmlInSlider&&!o.allowUnsafeHtmlInSlider?-1:4)}}var cn=function(e){return e[e.Low=0]="Low",e[e.High=1]="High",e[e.Floor=2]="Floor",e[e.Ceil=3]="Ceil",e[e.TickValue=4]="TickValue",e}(cn||{});class kc{floor=0;ceil=null;step=1;minRange=null;maxRange=null;pushRange=!1;minLimit=null;maxLimit=null;translate=null;combineLabels=null;getLegend=null;getStepLegend=null;stepsArray=null;bindIndexForStepsArray=!1;draggableRange=!1;draggableRangeOnly=!1;showSelectionBar=!1;showSelectionBarEnd=!1;showSelectionBarFromValue=null;showOuterSelectionBars=!1;hidePointerLabels=!1;hideLimitLabels=!1;autoHideLimitLabels=!0;readOnly=!1;disabled=!1;showTicks=!1;showTicksValues=!1;tickStep=null;tickValueStep=null;ticksArray=null;ticksTooltip=null;ticksValuesTooltip=null;vertical=!1;getSelectionBarColor=null;getTickColor=null;getPointerColor=null;keyboardSupport=!0;scale=1;rotate=0;enforceStep=!0;enforceRange=!0;enforceStepsArray=!0;noSwitching=!1;onlyBindHandles=!1;rightToLeft=!1;reversedControls=!1;boundPointerLabels=!0;logScale=!1;customValueToPosition=null;customPositionToValue=null;precisionLimit=12;selectionBarGradient=null;ariaLabel="ngx-slider";ariaLabelledBy=null;ariaLabelHigh="ngx-slider-max";ariaLabelledByHigh=null;handleDimension=null;barDimension=null;animate=!0;animateOnMove=!1}const nI=new R("AllowUnsafeHtmlInSlider");var L=function(e){return e[e.Min=0]="Min",e[e.Max=1]="Max",e}(L||{});class EU{value;highValue;pointerType}class M{static isNullOrUndefined(n){return null==n}static areArraysEqual(n,t){if(n.length!==t.length)return!1;for(let o=0;oMath.abs(n-r.value));let i=0;for(let r=0;r{r.events.next(a)};return n.addEventListener(t,s,{passive:!0,capture:!1}),r.teardownCallback=()=>{n.removeEventListener(t,s,{passive:!0,capture:!1})},r.eventsSubscription=r.events.pipe(M.isNullOrUndefined(i)?qM(()=>{}):WM(i,void 0,{leading:!0,trailing:!0})).subscribe(a=>{o(a)}),r}detachEventListener(n){M.isNullOrUndefined(n.eventsSubscription)||(n.eventsSubscription.unsubscribe(),n.eventsSubscription=null),M.isNullOrUndefined(n.events)||(n.events.complete(),n.events=null),M.isNullOrUndefined(n.teardownCallback)||(n.teardownCallback(),n.teardownCallback=null)}attachEventListener(n,t,o,i){const r=new oI;return r.eventName=t,r.events=new Xt,r.teardownCallback=this.renderer.listen(n,t,a=>{r.events.next(a)}),r.eventsSubscription=r.events.pipe(M.isNullOrUndefined(i)?qM(()=>{}):WM(i,void 0,{leading:!0,trailing:!0})).subscribe(a=>{o(a)}),r}}let so=(()=>{class e{elemRef=F(Nt);renderer=F(Sn);changeDetectionRef=F(Es);_position=0;get position(){return this._position}_dimension=0;get dimension(){return this._dimension}_alwaysHide=!1;get alwaysHide(){return this._alwaysHide}_vertical=!1;get vertical(){return this._vertical}_scale=1;get scale(){return this._scale}_rotate=0;get rotate(){return this._rotate}opacity=1;visibility="visible";left="";bottom="";height="";width="";transform="";eventListenerHelper;eventListeners=[];constructor(){this.eventListenerHelper=new iI(this.renderer)}setAlwaysHide(t){this._alwaysHide=t,this.visibility=t?"hidden":"visible"}hide(){this.opacity=0}show(){this.alwaysHide||(this.opacity=1)}isVisible(){return!this.alwaysHide&&0!==this.opacity}setVertical(t){this._vertical=t,this._vertical?(this.left="",this.width=""):(this.bottom="",this.height="")}setScale(t){this._scale=t}setRotate(t){this._rotate=t,this.transform="rotate("+t+"deg)"}getRotate(){return this._rotate}setPosition(t){this._position!==t&&!this.isRefDestroyed()&&this.changeDetectionRef.markForCheck(),this._position=t,this._vertical?this.bottom=Math.round(t)+"px":this.left=Math.round(t)+"px"}calculateDimension(){const t=this.getBoundingClientRect();this._dimension=this.vertical?(t.bottom-t.top)*this.scale:(t.right-t.left)*this.scale}setDimension(t){this._dimension!==t&&!this.isRefDestroyed()&&this.changeDetectionRef.markForCheck(),this._dimension=t,this._vertical?this.height=Math.round(t)+"px":this.width=Math.round(t)+"px"}getBoundingClientRect(){return this.elemRef.nativeElement.getBoundingClientRect()}on(t,o,i){const r=this.eventListenerHelper.attachEventListener(this.elemRef.nativeElement,t,o,i);this.eventListeners.push(r)}onPassive(t,o,i){const r=this.eventListenerHelper.attachPassiveEventListener(this.elemRef.nativeElement,t,o,i);this.eventListeners.push(r)}off(t){let o,i;M.isNullOrUndefined(t)?(o=[],i=this.eventListeners):(o=this.eventListeners.filter(r=>r.eventName!==t),i=this.eventListeners.filter(r=>r.eventName===t));for(const r of i)this.eventListenerHelper.detachEventListener(r);this.eventListeners=o}isRefDestroyed(){return M.isNullOrUndefined(this.changeDetectionRef)||this.changeDetectionRef.destroyed}static \u0275fac=function(o){return new(o||e)};static \u0275dir=W({type:e,selectors:[["","ngxSliderElement",""]],hostVars:14,hostBindings:function(o,i){2&o&&zl("opacity",i.opacity)("visibility",i.visibility)("left",i.left)("bottom",i.bottom)("height",i.height)("width",i.width)("transform",i.transform)},standalone:!1})}return e})(),Hg=(()=>{class e extends so{active=!1;role="";tabindex="";ariaOrientation="";ariaLabel="";ariaLabelledBy="";ariaValueNow="";ariaValueText="";ariaValueMin="";ariaValueMax="";focus(){this.elemRef.nativeElement.focus()}focusIfNeeded(){document.activeElement!==this.elemRef.nativeElement&&this.elemRef.nativeElement.focus()}static \u0275fac=(()=>{let t;return function(i){return(t||(t=ze(e)))(i||e)}})();static \u0275dir=W({type:e,selectors:[["","ngxSliderHandle",""]],hostVars:11,hostBindings:function(o,i){2&o&&(ct("role",i.role)("tabindex",i.tabindex)("aria-orientation",i.ariaOrientation)("aria-label",i.ariaLabel)("aria-labelledby",i.ariaLabelledBy)("aria-valuenow",i.ariaValueNow)("aria-valuetext",i.ariaValueText)("aria-valuemin",i.ariaValueMin)("aria-valuemax",i.ariaValueMax),An("ngx-slider-active",i.active))},standalone:!1,features:[ae]})}return e})(),ir=(()=>{class e extends so{allowUnsafeHtmlInSlider=F(nI,{optional:!0});_value=null;get value(){return this._value}setValue(t){let o=!1;!this.alwaysHide&&(M.isNullOrUndefined(this.value)||this.value.length!==t.length||this.value.length>0&&0===this.dimension)&&(o=!0),this._value=t,!1===this.allowUnsafeHtmlInSlider?this.elemRef.nativeElement.innerText=t:this.elemRef.nativeElement.innerHTML=t,o&&this.calculateDimension()}static \u0275fac=(()=>{let t;return function(i){return(t||(t=ze(e)))(i||e)}})();static \u0275dir=W({type:e,selectors:[["","ngxSliderLabel",""]],standalone:!1,features:[ae]})}return e})(),MU=(()=>{class e{template;tooltip;placement;content;static \u0275fac=function(o){return new(o||e)};static \u0275cmp=qt({type:e,selectors:[["ngx-slider-tooltip-wrapper"]],inputs:{template:"template",tooltip:"tooltip",placement:"placement",content:"content"},standalone:!1,decls:2,vars:2,consts:[[1,"ngx-slider-inner-tooltip"],[4,"ngTemplateOutlet","ngTemplateOutletContext"]],template:function(o,i){1&o&&(y(0,iU,1,6),y(1,rU,2,3,"div",0)),2&o&&(C(i.template?0:-1),f(),C(i.template?-1:1))},dependencies:[uE],styles:[".ngx-slider-inner-tooltip[_ngcontent-%COMP%]{height:100%}"]})}return e})();class IU{selected=!1;style={};tooltip=null;tooltipPlacement=null;value=null;valueTooltip=null;valueTooltipPlacement=null;legend=null}class rI{active=!1;value=0;difference=0;position=0;lowLimit=0;highLimit=0}class Fc{value;highValue;static compare(n,t){return!(M.isNullOrUndefined(n)&&M.isNullOrUndefined(t)||M.isNullOrUndefined(n)!==M.isNullOrUndefined(t))&&n.value===t.value&&n.highValue===t.highValue}}class sI extends Fc{forceChange;static compare(n,t){return!(M.isNullOrUndefined(n)&&M.isNullOrUndefined(t)||M.isNullOrUndefined(n)!==M.isNullOrUndefined(t))&&n.value===t.value&&n.highValue===t.highValue&&n.forceChange===t.forceChange}}const TU={provide:Kt,useExisting:ge(()=>aI),multi:!0};let aI=(()=>{class e{renderer=F(Sn);elementRef=F(Nt);changeDetectionRef=F(Es);zone=F(re);allowUnsafeHtmlInSlider=F(nI,{optional:!0});sliderElementNgxSliderClass=!0;value=null;valueChange=new ve;highValue=null;highValueChange=new ve;options=new kc;userChangeStart=new ve;userChange=new ve;userChangeEnd=new ve;manualRefreshSubscription;set manualRefresh(t){this.unsubscribeManualRefresh(),this.manualRefreshSubscription=t.subscribe(()=>{setTimeout(()=>this.calculateViewDimensionsAndDetectChanges())})}triggerFocusSubscription;set triggerFocus(t){this.unsubscribeTriggerFocus(),this.triggerFocusSubscription=t.subscribe(o=>{this.focusPointer(o)})}cancelUserChangeSubscription;set cancelUserChange(t){this.unsubscribeCancelUserChange(),this.cancelUserChangeSubscription=t.subscribe(()=>{this.moving&&(this.positionTrackingHandle(this.preStartHandleValue),this.forceEnd(!0))})}get range(){return!M.isNullOrUndefined(this.value)&&!M.isNullOrUndefined(this.highValue)}initHasRun=!1;inputModelChangeSubject=new Xt;inputModelChangeSubscription=null;outputModelChangeSubject=new Xt;outputModelChangeSubscription=null;viewLowValue=null;viewHighValue=null;viewOptions=new kc;handleHalfDimension=0;maxHandlePosition=0;currentTrackingPointer=null;currentFocusPointer=null;firstKeyDown=!1;touchId=null;dragging=new rI;preStartHandleValue=null;leftOuterSelectionBarElement;rightOuterSelectionBarElement;fullBarElement;selectionBarElement;minHandleElement;maxHandleElement;floorLabelElement;ceilLabelElement;minHandleLabelElement;maxHandleLabelElement;combinedLabelElement;ticksElement;tooltipTemplate;sliderElementVerticalClass=!1;sliderElementAnimateClass=!1;sliderElementWithLegendClass=!1;sliderElementDisabledAttr=null;sliderElementAriaLabel="ngx-slider";barStyle={};minPointerStyle={};maxPointerStyle={};fullBarTransparentClass=!1;selectionBarDraggableClass=!1;ticksUnderValuesClass=!1;get showTicks(){return this.viewOptions.showTicks}intermediateTicks=!1;ticks=[];eventListenerHelper=null;onMoveEventListener=null;onEndEventListener=null;moving=!1;resizeObserver=null;onTouchedCallback=null;onChangeCallback=null;constructor(){this.eventListenerHelper=new iI(this.renderer)}ngOnInit(){this.viewOptions=new kc,Object.assign(this.viewOptions,this.options),this.updateDisabledState(),this.updateVerticalState(),this.updateAriaLabel()}ngAfterViewInit(){this.applyOptions(),this.subscribeInputModelChangeSubject(),this.subscribeOutputModelChangeSubject(),this.renormaliseModelValues(),this.viewLowValue=this.modelValueToViewValue(this.value),this.viewHighValue=this.range?this.modelValueToViewValue(this.highValue):null,this.updateVerticalState(),this.manageElementsStyle(),this.updateDisabledState(),this.calculateViewDimensions(),this.addAccessibility(),this.updateCeilLabel(),this.updateFloorLabel(),this.initHandles(),this.manageEventsBindings(),this.updateAriaLabel(),this.subscribeResizeObserver(),this.initHasRun=!0,this.isRefDestroyed()||this.changeDetectionRef.detectChanges()}ngOnChanges(t){!M.isNullOrUndefined(t.options)&&JSON.stringify(t.options.previousValue)!==JSON.stringify(t.options.currentValue)&&this.onChangeOptions(),(!M.isNullOrUndefined(t.value)||!M.isNullOrUndefined(t.highValue))&&this.inputModelChangeSubject.next({value:this.value,highValue:this.highValue,controlAccessorChange:!1,forceChange:!1,internalChange:!1})}ngOnDestroy(){this.unbindEvents(),this.unsubscribeResizeObserver(),this.unsubscribeInputModelChangeSubject(),this.unsubscribeOutputModelChangeSubject(),this.unsubscribeManualRefresh(),this.unsubscribeTriggerFocus()}writeValue(t){t instanceof Array?(this.value=t[0],this.highValue=t[1]):this.value=t,this.inputModelChangeSubject.next({value:this.value,highValue:this.highValue,forceChange:!1,internalChange:!1,controlAccessorChange:!0})}registerOnChange(t){this.onChangeCallback=t}registerOnTouched(t){this.onTouchedCallback=t}setDisabledState(t){this.viewOptions.disabled=t,this.updateDisabledState(),this.initHasRun&&this.manageEventsBindings()}setAriaLabel(t){this.viewOptions.ariaLabel=t,this.updateAriaLabel()}onResize(t){this.calculateViewDimensionsAndDetectChanges()}subscribeInputModelChangeSubject(){this.inputModelChangeSubscription=this.inputModelChangeSubject.pipe(ZM(sI.compare),function Yj(e,n){return Mo((t,o)=>{let i=0;t.subscribe(jn(o,r=>e.call(n,r,i++)&&o.next(r)))})}(t=>!t.forceChange&&!t.internalChange)).subscribe(t=>this.applyInputModelChange(t))}subscribeOutputModelChangeSubject(){this.outputModelChangeSubscription=this.outputModelChangeSubject.pipe(ZM(sI.compare)).subscribe(t=>this.publishOutputModelChange(t))}subscribeResizeObserver(){ro.isResizeObserverAvailable()&&(this.resizeObserver=new ResizeObserver(()=>this.calculateViewDimensionsAndDetectChanges()),this.resizeObserver.observe(this.elementRef.nativeElement))}unsubscribeResizeObserver(){ro.isResizeObserverAvailable()&&null!==this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null)}unsubscribeOnMove(){M.isNullOrUndefined(this.onMoveEventListener)||(this.eventListenerHelper.detachEventListener(this.onMoveEventListener),this.onMoveEventListener=null)}unsubscribeOnEnd(){M.isNullOrUndefined(this.onEndEventListener)||(this.eventListenerHelper.detachEventListener(this.onEndEventListener),this.onEndEventListener=null)}unsubscribeInputModelChangeSubject(){M.isNullOrUndefined(this.inputModelChangeSubscription)||(this.inputModelChangeSubscription.unsubscribe(),this.inputModelChangeSubscription=null)}unsubscribeOutputModelChangeSubject(){M.isNullOrUndefined(this.outputModelChangeSubscription)||(this.outputModelChangeSubscription.unsubscribe(),this.outputModelChangeSubscription=null)}unsubscribeManualRefresh(){M.isNullOrUndefined(this.manualRefreshSubscription)||(this.manualRefreshSubscription.unsubscribe(),this.manualRefreshSubscription=null)}unsubscribeTriggerFocus(){M.isNullOrUndefined(this.triggerFocusSubscription)||(this.triggerFocusSubscription.unsubscribe(),this.triggerFocusSubscription=null)}unsubscribeCancelUserChange(){M.isNullOrUndefined(this.cancelUserChangeSubscription)||(this.cancelUserChangeSubscription.unsubscribe(),this.cancelUserChangeSubscription=null)}getPointerElement(t){return t===L.Min?this.minHandleElement:t===L.Max?this.maxHandleElement:null}getCurrentTrackingValue(){return this.currentTrackingPointer===L.Min?this.viewLowValue:this.currentTrackingPointer===L.Max?this.viewHighValue:null}modelValueToViewValue(t){return M.isNullOrUndefined(t)?NaN:M.isNullOrUndefined(this.viewOptions.stepsArray)||this.viewOptions.bindIndexForStepsArray?+t:M.findStepIndex(+t,this.viewOptions.stepsArray)}viewValueToModelValue(t){return M.isNullOrUndefined(this.viewOptions.stepsArray)||this.viewOptions.bindIndexForStepsArray?t:this.getStepValue(t)}getStepValue(t){const o=this.viewOptions.stepsArray[t];return M.isNullOrUndefined(o)?NaN:o.value}applyViewChange(){this.value=this.viewValueToModelValue(this.viewLowValue),this.range&&(this.highValue=this.viewValueToModelValue(this.viewHighValue)),this.outputModelChangeSubject.next({value:this.value,highValue:this.highValue,controlAccessorChange:!1,userEventInitiated:!0,forceChange:!1}),this.inputModelChangeSubject.next({value:this.value,highValue:this.highValue,controlAccessorChange:!1,forceChange:!1,internalChange:!0})}applyInputModelChange(t){const o=this.normaliseModelValues(t),i=!Fc.compare(t,o);i&&(this.value=o.value,this.highValue=o.highValue),this.viewLowValue=this.modelValueToViewValue(o.value),this.viewHighValue=this.range?this.modelValueToViewValue(o.highValue):null,this.updateLowHandle(this.valueToPosition(this.viewLowValue)),this.range&&this.updateHighHandle(this.valueToPosition(this.viewHighValue)),this.updateSelectionBar(),this.updateTicksScale(),this.updateAriaAttributes(),this.range&&this.updateCombinedLabel(),this.outputModelChangeSubject.next({value:o.value,highValue:o.highValue,controlAccessorChange:t.controlAccessorChange,forceChange:i,userEventInitiated:!1})}publishOutputModelChange(t){const o=()=>{this.valueChange.emit(t.value),this.range&&this.highValueChange.emit(t.highValue),!t.controlAccessorChange&&(M.isNullOrUndefined(this.onChangeCallback)||this.onChangeCallback(this.range?[t.value,t.highValue]:t.value),M.isNullOrUndefined(this.onTouchedCallback)||this.onTouchedCallback(this.range?[t.value,t.highValue]:t.value))};t.userEventInitiated?(o(),this.userChange.emit(this.getChangeContext())):setTimeout(()=>{o()})}normaliseModelValues(t){const o=new Fc;if(o.value=t.value,o.highValue=t.highValue,!M.isNullOrUndefined(this.viewOptions.stepsArray)){if(this.viewOptions.enforceStepsArray){const i=M.findStepIndex(o.value,this.viewOptions.stepsArray);if(o.value=this.viewOptions.stepsArray[i].value,this.range){const r=M.findStepIndex(o.highValue,this.viewOptions.stepsArray);o.highValue=this.viewOptions.stepsArray[r].value}}return o}if(this.viewOptions.enforceStep&&(o.value=this.roundStep(o.value),this.range&&(o.highValue=this.roundStep(o.highValue))),this.viewOptions.enforceRange&&(o.value=Re.clampToRange(o.value,this.viewOptions.floor,this.viewOptions.ceil),this.range&&(o.highValue=Re.clampToRange(o.highValue,this.viewOptions.floor,this.viewOptions.ceil)),this.range&&t.value>t.highValue))if(this.viewOptions.noSwitching)o.value=o.highValue;else{const i=t.value;o.value=t.highValue,o.highValue=i}return o}renormaliseModelValues(){const t={value:this.value,highValue:this.highValue},o=this.normaliseModelValues(t);Fc.compare(o,t)||(this.value=o.value,this.highValue=o.highValue,this.outputModelChangeSubject.next({value:this.value,highValue:this.highValue,controlAccessorChange:!1,forceChange:!0,userEventInitiated:!1}))}onChangeOptions(){if(!this.initHasRun)return;const t=this.getOptionsInfluencingEventBindings(this.viewOptions);this.applyOptions();const o=this.getOptionsInfluencingEventBindings(this.viewOptions),i=!M.areArraysEqual(t,o);this.renormaliseModelValues(),this.viewLowValue=this.modelValueToViewValue(this.value),this.viewHighValue=this.range?this.modelValueToViewValue(this.highValue):null,this.resetSlider(i)}applyOptions(){if(this.viewOptions=new kc,Object.assign(this.viewOptions,this.options),this.viewOptions.draggableRange=this.range&&this.viewOptions.draggableRange,this.viewOptions.draggableRangeOnly=this.range&&this.viewOptions.draggableRangeOnly,this.viewOptions.draggableRangeOnly&&(this.viewOptions.draggableRange=!0),this.viewOptions.showTicks=this.viewOptions.showTicks||this.viewOptions.showTicksValues||!M.isNullOrUndefined(this.viewOptions.ticksArray),this.viewOptions.showTicks&&(!M.isNullOrUndefined(this.viewOptions.tickStep)||!M.isNullOrUndefined(this.viewOptions.ticksArray))&&(this.intermediateTicks=!0),this.viewOptions.showSelectionBar=this.viewOptions.showSelectionBar||this.viewOptions.showSelectionBarEnd||!M.isNullOrUndefined(this.viewOptions.showSelectionBarFromValue),M.isNullOrUndefined(this.viewOptions.stepsArray)?this.applyFloorCeilOptions():this.applyStepsArrayOptions(),M.isNullOrUndefined(this.viewOptions.combineLabels)&&(this.viewOptions.combineLabels=(t,o)=>t+" - "+o),this.viewOptions.logScale&&0===this.viewOptions.floor)throw Error("Can't use floor=0 with logarithmic scale")}applyStepsArrayOptions(){this.viewOptions.floor=0,this.viewOptions.ceil=this.viewOptions.stepsArray.length-1,this.viewOptions.step=1,M.isNullOrUndefined(this.viewOptions.translate)&&(this.viewOptions.translate=t=>String(this.viewOptions.bindIndexForStepsArray?this.getStepValue(t):t))}applyFloorCeilOptions(){if(M.isNullOrUndefined(this.viewOptions.step)?this.viewOptions.step=1:(this.viewOptions.step=+this.viewOptions.step,this.viewOptions.step<=0&&(this.viewOptions.step=1)),M.isNullOrUndefined(this.viewOptions.ceil)||M.isNullOrUndefined(this.viewOptions.floor))throw Error("floor and ceil options must be supplied");this.viewOptions.ceil=+this.viewOptions.ceil,this.viewOptions.floor=+this.viewOptions.floor,M.isNullOrUndefined(this.viewOptions.translate)&&(this.viewOptions.translate=t=>String(t))}resetSlider(t=!0){this.manageElementsStyle(),this.addAccessibility(),this.updateCeilLabel(),this.updateFloorLabel(),t&&(this.unbindEvents(),this.manageEventsBindings()),this.updateDisabledState(),this.updateAriaLabel(),this.calculateViewDimensions(),this.refocusPointerIfNeeded()}focusPointer(t){t!==L.Min&&t!==L.Max&&(t=L.Min),t===L.Min?this.minHandleElement.focus():this.range&&t===L.Max&&this.maxHandleElement.focus()}refocusPointerIfNeeded(){M.isNullOrUndefined(this.currentFocusPointer)||this.getPointerElement(this.currentFocusPointer).focusIfNeeded()}manageElementsStyle(){this.updateScale(),this.floorLabelElement.setAlwaysHide(this.viewOptions.showTicksValues||this.viewOptions.hideLimitLabels),this.ceilLabelElement.setAlwaysHide(this.viewOptions.showTicksValues||this.viewOptions.hideLimitLabels);const t=this.viewOptions.showTicksValues&&!this.intermediateTicks;this.minHandleLabelElement.setAlwaysHide(t||this.viewOptions.hidePointerLabels),this.maxHandleLabelElement.setAlwaysHide(t||!this.range||this.viewOptions.hidePointerLabels),this.combinedLabelElement.setAlwaysHide(t||!this.range||this.viewOptions.hidePointerLabels),this.selectionBarElement.setAlwaysHide(!this.range&&!this.viewOptions.showSelectionBar),this.leftOuterSelectionBarElement.setAlwaysHide(!this.range||!this.viewOptions.showOuterSelectionBars),this.rightOuterSelectionBarElement.setAlwaysHide(!this.range||!this.viewOptions.showOuterSelectionBars),this.fullBarTransparentClass=this.range&&this.viewOptions.showOuterSelectionBars,this.selectionBarDraggableClass=this.viewOptions.draggableRange&&!this.viewOptions.onlyBindHandles,this.ticksUnderValuesClass=this.intermediateTicks&&this.options.showTicksValues,this.sliderElementVerticalClass!==this.viewOptions.vertical&&(this.updateVerticalState(),setTimeout(()=>{this.resetSlider()})),this.sliderElementAnimateClass!==this.viewOptions.animate&&setTimeout(()=>{this.sliderElementAnimateClass=this.viewOptions.animate}),this.updateRotate()}manageEventsBindings(){this.viewOptions.disabled||this.viewOptions.readOnly?this.unbindEvents():this.bindEvents()}updateDisabledState(){this.sliderElementDisabledAttr=this.viewOptions.disabled?"disabled":null}updateAriaLabel(){this.sliderElementAriaLabel=this.viewOptions.ariaLabel||"nxg-slider"}updateVerticalState(){this.sliderElementVerticalClass=this.viewOptions.vertical;for(const t of this.getAllSliderElements())M.isNullOrUndefined(t)||t.setVertical(this.viewOptions.vertical)}updateScale(){for(const t of this.getAllSliderElements())t.setScale(this.viewOptions.scale)}updateRotate(){for(const t of this.getAllSliderElements())t.setRotate(this.viewOptions.rotate)}getAllSliderElements(){return[this.leftOuterSelectionBarElement,this.rightOuterSelectionBarElement,this.fullBarElement,this.selectionBarElement,this.minHandleElement,this.maxHandleElement,this.floorLabelElement,this.ceilLabelElement,this.minHandleLabelElement,this.maxHandleLabelElement,this.combinedLabelElement,this.ticksElement]}initHandles(){this.updateLowHandle(this.valueToPosition(this.viewLowValue)),this.range&&this.updateHighHandle(this.valueToPosition(this.viewHighValue)),this.updateSelectionBar(),this.range&&this.updateCombinedLabel(),this.updateTicksScale()}addAccessibility(){this.updateAriaAttributes(),this.minHandleElement.role="slider",this.minHandleElement.tabindex=!this.viewOptions.keyboardSupport||this.viewOptions.readOnly||this.viewOptions.disabled?"":"0",this.minHandleElement.ariaOrientation=this.viewOptions.vertical||0!==this.viewOptions.rotate?"vertical":"horizontal",M.isNullOrUndefined(this.viewOptions.ariaLabel)?M.isNullOrUndefined(this.viewOptions.ariaLabelledBy)||(this.minHandleElement.ariaLabelledBy=this.viewOptions.ariaLabelledBy):this.minHandleElement.ariaLabel=this.viewOptions.ariaLabel,this.range&&(this.maxHandleElement.role="slider",this.maxHandleElement.tabindex=!this.viewOptions.keyboardSupport||this.viewOptions.readOnly||this.viewOptions.disabled?"":"0",this.maxHandleElement.ariaOrientation=this.viewOptions.vertical||0!==this.viewOptions.rotate?"vertical":"horizontal",M.isNullOrUndefined(this.viewOptions.ariaLabelHigh)?M.isNullOrUndefined(this.viewOptions.ariaLabelledByHigh)||(this.maxHandleElement.ariaLabelledBy=this.viewOptions.ariaLabelledByHigh):this.maxHandleElement.ariaLabel=this.viewOptions.ariaLabelHigh)}updateAriaAttributes(){this.minHandleElement.ariaValueNow=(+this.value).toString(),this.minHandleElement.ariaValueText=this.viewOptions.translate(+this.value,cn.Low),this.minHandleElement.ariaValueMin=this.viewOptions.floor.toString(),this.minHandleElement.ariaValueMax=this.viewOptions.ceil.toString(),this.range&&(this.maxHandleElement.ariaValueNow=(+this.highValue).toString(),this.maxHandleElement.ariaValueText=this.viewOptions.translate(+this.highValue,cn.High),this.maxHandleElement.ariaValueMin=this.viewOptions.floor.toString(),this.maxHandleElement.ariaValueMax=this.viewOptions.ceil.toString())}calculateViewDimensions(){M.isNullOrUndefined(this.viewOptions.handleDimension)?this.minHandleElement.calculateDimension():this.minHandleElement.setDimension(this.viewOptions.handleDimension);const t=this.minHandleElement.dimension;this.handleHalfDimension=t/2,M.isNullOrUndefined(this.viewOptions.barDimension)?this.fullBarElement.calculateDimension():this.fullBarElement.setDimension(this.viewOptions.barDimension),this.maxHandlePosition=this.fullBarElement.dimension-t,this.initHasRun&&(this.updateFloorLabel(),this.updateCeilLabel(),this.initHandles())}calculateViewDimensionsAndDetectChanges(){this.calculateViewDimensions(),this.isRefDestroyed()||this.changeDetectionRef.detectChanges()}isRefDestroyed(){return this.changeDetectionRef.destroyed}updateTicksScale(){if(!this.viewOptions.showTicks&&this.sliderElementWithLegendClass)return void setTimeout(()=>{this.sliderElementWithLegendClass=!1});const t=M.isNullOrUndefined(this.viewOptions.ticksArray)?this.getTicksArray():this.viewOptions.ticksArray,o=this.viewOptions.vertical?"translateY":"translateX";this.viewOptions.rightToLeft&&t.reverse();const i=M.isNullOrUndefined(this.viewOptions.tickValueStep)?M.isNullOrUndefined(this.viewOptions.tickStep)?this.viewOptions.step:this.viewOptions.tickStep:this.viewOptions.tickValueStep;let r=!1;const s=t.map(a=>{let l=this.valueToPosition(a);this.viewOptions.vertical&&(l=this.maxHandlePosition-l);const c=o+"("+Math.round(l)+"px)",u=new IU;u.selected=this.isTickSelected(a),u.style={"-webkit-transform":c,"-moz-transform":c,"-o-transform":c,"-ms-transform":c,transform:c},u.selected&&!M.isNullOrUndefined(this.viewOptions.getSelectionBarColor)&&(u.style["background-color"]=this.getSelectionBarColor()),!u.selected&&!M.isNullOrUndefined(this.viewOptions.getTickColor)&&(u.style["background-color"]=this.getTickColor(a)),M.isNullOrUndefined(this.viewOptions.ticksTooltip)||(u.tooltip=this.viewOptions.ticksTooltip(a),u.tooltipPlacement=this.viewOptions.vertical?"right":"top"),this.viewOptions.showTicksValues&&!M.isNullOrUndefined(i)&&Re.isModuloWithinPrecisionLimit(a,i,this.viewOptions.precisionLimit)&&(u.value=this.getDisplayValue(a,cn.TickValue),M.isNullOrUndefined(this.viewOptions.ticksValuesTooltip)||(u.valueTooltip=this.viewOptions.ticksValuesTooltip(a),u.valueTooltipPlacement=this.viewOptions.vertical?"right":"top"));let d=null;if(M.isNullOrUndefined(this.viewOptions.stepsArray))M.isNullOrUndefined(this.viewOptions.getLegend)||(d=this.viewOptions.getLegend(a));else{const g=this.viewOptions.stepsArray[a];M.isNullOrUndefined(this.viewOptions.getStepLegend)?M.isNullOrUndefined(g)||(d=g.legend):d=this.viewOptions.getStepLegend(g)}return M.isNullOrUndefined(d)||(u.legend=d,r=!0),u});if(this.sliderElementWithLegendClass!==r&&setTimeout(()=>{this.sliderElementWithLegendClass=r}),M.isNullOrUndefined(this.ticks)||this.ticks.length!==s.length)this.ticks=s,this.isRefDestroyed()||this.changeDetectionRef.detectChanges();else for(let a=0;a=this.viewLowValue)return!0}else if(this.viewOptions.showSelectionBar&&t<=this.viewLowValue)return!0}else{const o=this.viewOptions.showSelectionBarFromValue;if(this.viewLowValue>o&&t>=o&&t<=this.viewLowValue)return!0;if(this.viewLowValue=this.viewLowValue)return!0}return!!(this.range&&t>=this.viewLowValue&&t<=this.viewHighValue)}updateFloorLabel(){this.floorLabelElement.alwaysHide||(this.floorLabelElement.setValue(this.getDisplayValue(this.viewOptions.floor,cn.Floor)),this.floorLabelElement.calculateDimension(),this.floorLabelElement.setPosition(this.viewOptions.rightToLeft?this.fullBarElement.dimension-this.floorLabelElement.dimension:0))}updateCeilLabel(){this.ceilLabelElement.alwaysHide||(this.ceilLabelElement.setValue(this.getDisplayValue(this.viewOptions.ceil,cn.Ceil)),this.ceilLabelElement.calculateDimension(),this.ceilLabelElement.setPosition(this.viewOptions.rightToLeft?0:this.fullBarElement.dimension-this.ceilLabelElement.dimension))}updateHandles(t,o){t===L.Min?this.updateLowHandle(o):t===L.Max&&this.updateHighHandle(o),this.updateSelectionBar(),this.updateTicksScale(),this.range&&this.updateCombinedLabel()}getHandleLabelPos(t,o){const i=t===L.Min?this.minHandleLabelElement.dimension:this.maxHandleLabelElement.dimension,r=o-i/2+this.handleHalfDimension,s=this.fullBarElement.dimension-i;return this.viewOptions.boundPointerLabels?this.viewOptions.rightToLeft&&t===L.Min||!this.viewOptions.rightToLeft&&t===L.Max?Math.min(r,s):Math.min(Math.max(r,0),s):r}updateLowHandle(t){this.minHandleElement.setPosition(t),this.minHandleLabelElement.setValue(this.getDisplayValue(this.viewLowValue,cn.Low)),this.minHandleLabelElement.setPosition(this.getHandleLabelPos(L.Min,t)),M.isNullOrUndefined(this.viewOptions.getPointerColor)||(this.minPointerStyle={backgroundColor:this.getPointerColor(L.Min)}),this.viewOptions.autoHideLimitLabels&&this.updateFloorAndCeilLabelsVisibility()}updateHighHandle(t){this.maxHandleElement.setPosition(t),this.maxHandleLabelElement.setValue(this.getDisplayValue(this.viewHighValue,cn.High)),this.maxHandleLabelElement.setPosition(this.getHandleLabelPos(L.Max,t)),M.isNullOrUndefined(this.viewOptions.getPointerColor)||(this.maxPointerStyle={backgroundColor:this.getPointerColor(L.Max)}),this.viewOptions.autoHideLimitLabels&&this.updateFloorAndCeilLabelsVisibility()}updateFloorAndCeilLabelsVisibility(){if(this.viewOptions.hidePointerLabels)return;let t=!1,o=!1;const i=this.isLabelBelowFloorLabel(this.minHandleLabelElement),r=this.isLabelAboveCeilLabel(this.minHandleLabelElement),s=this.isLabelAboveCeilLabel(this.maxHandleLabelElement),a=this.isLabelBelowFloorLabel(this.combinedLabelElement),l=this.isLabelAboveCeilLabel(this.combinedLabelElement);if(i?(t=!0,this.floorLabelElement.hide()):(t=!1,this.floorLabelElement.show()),r?(o=!0,this.ceilLabelElement.hide()):(o=!1,this.ceilLabelElement.show()),this.range){const c=this.combinedLabelElement.isVisible()?l:s,u=this.combinedLabelElement.isVisible()?a:i;c?this.ceilLabelElement.hide():o||this.ceilLabelElement.show(),u?this.floorLabelElement.hide():t||this.floorLabelElement.show()}}isLabelBelowFloorLabel(t){const o=t.position,r=this.floorLabelElement.position;return this.viewOptions.rightToLeft?o+t.dimension>=r-2:o<=r+this.floorLabelElement.dimension+2}isLabelAboveCeilLabel(t){const o=t.position,r=this.ceilLabelElement.position;return this.viewOptions.rightToLeft?o<=r+this.ceilLabelElement.dimension+2:o+t.dimension>=r-2}updateSelectionBar(){let t=0,o=0;const i=this.viewOptions.rightToLeft?!this.viewOptions.showSelectionBarEnd:this.viewOptions.showSelectionBarEnd,r=this.viewOptions.rightToLeft?this.maxHandleElement.position+this.handleHalfDimension:this.minHandleElement.position+this.handleHalfDimension;if(this.range)o=Math.abs(this.maxHandleElement.position-this.minHandleElement.position),t=r;else if(M.isNullOrUndefined(this.viewOptions.showSelectionBarFromValue))i?(o=Math.ceil(Math.abs(this.maxHandlePosition-this.minHandleElement.position)+this.handleHalfDimension),t=Math.floor(this.minHandleElement.position+this.handleHalfDimension)):(o=this.minHandleElement.position+this.handleHalfDimension,t=0);else{const s=this.viewOptions.showSelectionBarFromValue,a=this.valueToPosition(s);(this.viewOptions.rightToLeft?this.viewLowValue<=s:this.viewLowValue>s)?(o=this.minHandleElement.position-a,t=a+this.handleHalfDimension):(o=a-this.minHandleElement.position,t=this.minHandleElement.position+this.handleHalfDimension)}if(this.selectionBarElement.setDimension(o),this.selectionBarElement.setPosition(t),this.range&&this.viewOptions.showOuterSelectionBars&&(this.viewOptions.rightToLeft?(this.rightOuterSelectionBarElement.setDimension(t),this.rightOuterSelectionBarElement.setPosition(0),this.fullBarElement.calculateDimension(),this.leftOuterSelectionBarElement.setDimension(this.fullBarElement.dimension-(t+o)),this.leftOuterSelectionBarElement.setPosition(t+o)):(this.leftOuterSelectionBarElement.setDimension(t),this.leftOuterSelectionBarElement.setPosition(0),this.fullBarElement.calculateDimension(),this.rightOuterSelectionBarElement.setDimension(this.fullBarElement.dimension-(t+o)),this.rightOuterSelectionBarElement.setPosition(t+o))),M.isNullOrUndefined(this.viewOptions.getSelectionBarColor)){if(!M.isNullOrUndefined(this.viewOptions.selectionBarGradient)){const s=M.isNullOrUndefined(this.viewOptions.showSelectionBarFromValue)?0:this.valueToPosition(this.viewOptions.showSelectionBarFromValue),a=s-t>0&&!i||s-t<=0&&i;this.barStyle={backgroundImage:"linear-gradient(to "+(this.viewOptions.vertical?a?"bottom":"top":a?"left":"right")+", "+this.viewOptions.selectionBarGradient.from+" 0%,"+this.viewOptions.selectionBarGradient.to+" 100%)"},this.viewOptions.vertical?(this.barStyle.backgroundPosition="center "+(s+o+t+(a?-this.handleHalfDimension:0))+"px",this.barStyle.backgroundSize="100% "+(this.fullBarElement.dimension-this.handleHalfDimension)+"px"):(this.barStyle.backgroundPosition=s-t+(a?this.handleHalfDimension:0)+"px center",this.barStyle.backgroundSize=this.fullBarElement.dimension-this.handleHalfDimension+"px 100%")}}else{const s=this.getSelectionBarColor();this.barStyle={backgroundColor:s}}}getSelectionBarColor(){return this.range?this.viewOptions.getSelectionBarColor(this.value,this.highValue):this.viewOptions.getSelectionBarColor(this.value)}getPointerColor(t){return this.viewOptions.getPointerColor(t===L.Max?this.highValue:this.value,t)}getTickColor(t){return this.viewOptions.getTickColor(t)}updateCombinedLabel(){let t=null;if(t=this.viewOptions.rightToLeft?this.minHandleLabelElement.position-this.minHandleLabelElement.dimension-10<=this.maxHandleLabelElement.position:this.minHandleLabelElement.position+this.minHandleLabelElement.dimension+10>=this.maxHandleLabelElement.position,t){const o=this.getDisplayValue(this.viewLowValue,cn.Low),i=this.getDisplayValue(this.viewHighValue,cn.High),r=this.viewOptions.rightToLeft?this.viewOptions.combineLabels(i,o):this.viewOptions.combineLabels(o,i);this.combinedLabelElement.setValue(r);const s=this.viewOptions.boundPointerLabels?Math.min(Math.max(this.selectionBarElement.position+this.selectionBarElement.dimension/2-this.combinedLabelElement.dimension/2,0),this.fullBarElement.dimension-this.combinedLabelElement.dimension):this.selectionBarElement.position+this.selectionBarElement.dimension/2-this.combinedLabelElement.dimension/2;this.combinedLabelElement.setPosition(s),this.minHandleLabelElement.hide(),this.maxHandleLabelElement.hide(),this.combinedLabelElement.show()}else this.updateHighHandle(this.valueToPosition(this.viewHighValue)),this.updateLowHandle(this.valueToPosition(this.viewLowValue)),this.maxHandleLabelElement.show(),this.minHandleLabelElement.show(),this.combinedLabelElement.hide();this.viewOptions.autoHideLimitLabels&&this.updateFloorAndCeilLabelsVisibility()}getDisplayValue(t,o){return!M.isNullOrUndefined(this.viewOptions.stepsArray)&&!this.viewOptions.bindIndexForStepsArray&&(t=this.getStepValue(t)),this.viewOptions.translate(t,o)}roundStep(t,o){const i=M.isNullOrUndefined(o)?this.viewOptions.step:o;let r=Re.roundToPrecisionLimit((t-this.viewOptions.floor)/i,this.viewOptions.precisionLimit);return r=Math.round(r)*i,Re.roundToPrecisionLimit(this.viewOptions.floor+r,this.viewOptions.precisionLimit)}valueToPosition(t){let o=M.linearValueToPosition;M.isNullOrUndefined(this.viewOptions.customValueToPosition)?this.viewOptions.logScale&&(o=M.logValueToPosition):o=this.viewOptions.customValueToPosition;let i=o(t=Re.clampToRange(t,this.viewOptions.floor,this.viewOptions.ceil),this.viewOptions.floor,this.viewOptions.ceil);return M.isNullOrUndefined(i)&&(i=0),this.viewOptions.rightToLeft&&(i=1-i),i*this.maxHandlePosition}positionToValue(t){let o=t/this.maxHandlePosition;this.viewOptions.rightToLeft&&(o=1-o);let i=M.linearPositionToValue;M.isNullOrUndefined(this.viewOptions.customPositionToValue)?this.viewOptions.logScale&&(i=M.logPositionToValue):i=this.viewOptions.customPositionToValue;const r=i(o,this.viewOptions.floor,this.viewOptions.ceil);return M.isNullOrUndefined(r)?0:r}getEventXY(t,o){if(t instanceof MouseEvent)return this.viewOptions.vertical||0!==this.viewOptions.rotate?t.clientY:t.clientX;let i=0;const r=t.touches;if(!M.isNullOrUndefined(o))for(let s=0;sr?L.Max:this.viewOptions.rightToLeft?o>this.minHandleElement.position?L.Min:L.Max:othis.onBarStart(null,t,o,!0,!0,!0)),this.viewOptions.draggableRangeOnly?(this.minHandleElement.on("mousedown",o=>this.onBarStart(L.Min,t,o,!0,!0)),this.maxHandleElement.on("mousedown",o=>this.onBarStart(L.Max,t,o,!0,!0))):(this.minHandleElement.on("mousedown",o=>this.onStart(L.Min,o,!0,!0)),this.range&&this.maxHandleElement.on("mousedown",o=>this.onStart(L.Max,o,!0,!0)),this.viewOptions.onlyBindHandles||(this.fullBarElement.on("mousedown",o=>this.onStart(null,o,!0,!0,!0)),this.ticksElement.on("mousedown",o=>this.onStart(null,o,!0,!0,!0,!0)))),this.viewOptions.onlyBindHandles||this.selectionBarElement.onPassive("touchstart",o=>this.onBarStart(null,t,o,!0,!0,!0)),this.viewOptions.draggableRangeOnly?(this.minHandleElement.onPassive("touchstart",o=>this.onBarStart(L.Min,t,o,!0,!0)),this.maxHandleElement.onPassive("touchstart",o=>this.onBarStart(L.Max,t,o,!0,!0))):(this.minHandleElement.onPassive("touchstart",o=>this.onStart(L.Min,o,!0,!0)),this.range&&this.maxHandleElement.onPassive("touchstart",o=>this.onStart(L.Max,o,!0,!0)),this.viewOptions.onlyBindHandles||(this.fullBarElement.onPassive("touchstart",o=>this.onStart(null,o,!0,!0,!0)),this.ticksElement.onPassive("touchstart",o=>this.onStart(null,o,!1,!1,!0,!0)))),this.viewOptions.keyboardSupport&&(this.minHandleElement.on("focus",()=>this.onPointerFocus(L.Min)),this.range&&this.maxHandleElement.on("focus",()=>this.onPointerFocus(L.Max)))}getOptionsInfluencingEventBindings(t){return[t.disabled,t.readOnly,t.draggableRange,t.draggableRangeOnly,t.onlyBindHandles,t.keyboardSupport]}unbindEvents(){this.unsubscribeOnMove(),this.unsubscribeOnEnd();for(const t of this.getAllSliderElements())M.isNullOrUndefined(t)||t.off()}onBarStart(t,o,i,r,s,a,l){o?this.onDragStart(t,i,r,s):this.onStart(t,i,r,s,a,l)}onStart(t,o,i,r,s,a){o.stopPropagation(),!ro.isTouchEvent(o)&&!KM&&o.preventDefault(),this.moving=!1,this.calculateViewDimensions(),M.isNullOrUndefined(t)&&(t=this.getNearestHandle(o)),this.currentTrackingPointer=t;const l=this.getPointerElement(t);if(l.active=!0,this.preStartHandleValue=this.getCurrentTrackingValue(),this.viewOptions.keyboardSupport&&l.focus(),i){this.unsubscribeOnMove();const c=u=>this.dragging.active?this.onDragMove(u):this.onMove(u);this.onMoveEventListener=ro.isTouchEvent(o)?this.eventListenerHelper.attachPassiveEventListener(document,"touchmove",c):this.eventListenerHelper.attachEventListener(document,"mousemove",c)}if(r){this.unsubscribeOnEnd();const c=u=>this.onEnd(u);this.onEndEventListener=ro.isTouchEvent(o)?this.eventListenerHelper.attachPassiveEventListener(document,"touchend",c):this.eventListenerHelper.attachEventListener(document,"mouseup",c)}this.userChangeStart.emit(this.getChangeContext()),ro.isTouchEvent(o)&&!M.isNullOrUndefined(o.changedTouches)&&M.isNullOrUndefined(this.touchId)&&(this.touchId=o.changedTouches[0].identifier),s&&this.onMove(o,!0),a&&this.onEnd(o)}onMove(t,o){let i=null;if(ro.isTouchEvent(t)){const c=t.changedTouches;for(let u=0;u=this.maxHandlePosition?s=this.viewOptions.rightToLeft?this.viewOptions.floor:this.viewOptions.ceil:(s=this.positionToValue(r),s=o&&!M.isNullOrUndefined(this.viewOptions.tickStep)?this.roundStep(s,this.viewOptions.tickStep):this.roundStep(s)),this.positionTrackingHandle(s)}forceEnd(t=!1){this.moving=!1,this.viewOptions.animate&&(this.sliderElementAnimateClass=!0),t&&(this.sliderElementAnimateClass=!1,setTimeout(()=>{this.sliderElementAnimateClass=this.viewOptions.animate})),this.touchId=null,this.viewOptions.keyboardSupport||(this.minHandleElement.active=!1,this.maxHandleElement.active=!1,this.currentTrackingPointer=null),this.dragging.active=!1,this.unsubscribeOnMove(),this.unsubscribeOnEnd(),this.userChangeEnd.emit(this.getChangeContext())}onEnd(t){ro.isTouchEvent(t)&&t.changedTouches[0].identifier!==this.touchId||this.forceEnd()}onPointerFocus(t){const o=this.getPointerElement(t);o.on("blur",()=>this.onPointerBlur(o)),o.on("keydown",i=>this.onKeyboardEvent(i)),o.on("keyup",()=>this.onKeyUp()),o.active=!0,this.currentTrackingPointer=t,this.currentFocusPointer=t,this.firstKeyDown=!0}onKeyUp(){this.firstKeyDown=!0,this.userChangeEnd.emit(this.getChangeContext())}onPointerBlur(t){t.off("blur"),t.off("keydown"),t.off("keyup"),t.active=!1,M.isNullOrUndefined(this.touchId)&&(this.currentTrackingPointer=null,this.currentFocusPointer=null)}getKeyActions(t){const o=this.viewOptions.ceil-this.viewOptions.floor;let i=t+this.viewOptions.step,r=t-this.viewOptions.step,s=t+o/10,a=t-o/10;this.viewOptions.reversedControls&&(i=t-this.viewOptions.step,r=t+this.viewOptions.step,s=t-o/10,a=t+o/10);const l={UP:i,DOWN:r,LEFT:r,RIGHT:i,PAGEUP:s,PAGEDOWN:a,HOME:this.viewOptions.reversedControls?this.viewOptions.ceil:this.viewOptions.floor,END:this.viewOptions.reversedControls?this.viewOptions.floor:this.viewOptions.ceil};return this.viewOptions.rightToLeft&&(l.LEFT=i,l.RIGHT=r,(this.viewOptions.vertical||0!==this.viewOptions.rotate)&&(l.UP=r,l.DOWN=i)),l}onKeyboardEvent(t){const o=this.getCurrentTrackingValue(),i=M.isNullOrUndefined(t.keyCode)?t.which:t.keyCode,l=this.getKeyActions(o)[{38:"UP",40:"DOWN",37:"LEFT",39:"RIGHT",33:"PAGEUP",34:"PAGEDOWN",36:"HOME",35:"END"}[i]];if(M.isNullOrUndefined(l)||M.isNullOrUndefined(this.currentTrackingPointer))return;t.preventDefault(),this.firstKeyDown&&(this.firstKeyDown=!1,this.userChangeStart.emit(this.getChangeContext()));const c=Re.clampToRange(l,this.viewOptions.floor,this.viewOptions.ceil),u=this.roundStep(c);if(this.viewOptions.draggableRangeOnly){const d=this.viewHighValue-this.viewLowValue;let g,h;this.currentTrackingPointer===L.Min?(g=u,h=u+d,h>this.viewOptions.ceil&&(h=this.viewOptions.ceil,g=h-d)):this.currentTrackingPointer===L.Max&&(h=u,g=u-d,g=this.maxHandlePosition-i;let u,d;if(o<=r){if(0===s.position)return;u=this.getMinValue(o,!0,!1),d=this.getMaxValue(o,!0,!1)}else if(c){if(a.position===this.maxHandlePosition)return;d=this.getMaxValue(o,!0,!0),u=this.getMinValue(o,!0,!0)}else u=this.getMinValue(o,!1,!1),d=this.getMaxValue(o,!1,!1);this.positionTrackingBar(u,d)}positionTrackingBar(t,o){!M.isNullOrUndefined(this.viewOptions.minLimit)&&tthis.viewOptions.maxLimit&&(t=Re.roundToPrecisionLimit((o=this.viewOptions.maxLimit)-this.dragging.difference,this.viewOptions.precisionLimit)),this.viewLowValue=t,this.viewHighValue=o,this.applyViewChange(),this.updateHandles(L.Min,this.valueToPosition(t)),this.updateHandles(L.Max,this.valueToPosition(o))}positionTrackingHandle(t){t=this.applyMinMaxLimit(t),this.range&&(this.viewOptions.pushRange?t=this.applyPushRange(t):(this.viewOptions.noSwitching&&(this.currentTrackingPointer===L.Min&&t>this.viewHighValue?t=this.applyMinMaxRange(this.viewHighValue):this.currentTrackingPointer===L.Max&&tthis.viewHighValue?(this.viewLowValue=this.viewHighValue,this.applyViewChange(),this.updateHandles(L.Min,this.maxHandleElement.position),this.updateAriaAttributes(),this.currentTrackingPointer=L.Max,this.minHandleElement.active=!1,this.maxHandleElement.active=!0,this.viewOptions.keyboardSupport&&this.maxHandleElement.focus()):this.currentTrackingPointer===L.Max&&tthis.viewOptions.maxLimit?this.viewOptions.maxLimit:t}applyMinMaxRange(t){const i=Math.abs(t-(this.currentTrackingPointer===L.Min?this.viewHighValue:this.viewLowValue));if(!M.isNullOrUndefined(this.viewOptions.minRange)&&ithis.viewOptions.maxRange){if(this.currentTrackingPointer===L.Min)return Re.roundToPrecisionLimit(this.viewHighValue-this.viewOptions.maxRange,this.viewOptions.precisionLimit);if(this.currentTrackingPointer===L.Max)return Re.roundToPrecisionLimit(this.viewLowValue+this.viewOptions.maxRange,this.viewOptions.precisionLimit)}return t}applyPushRange(t){const o=this.currentTrackingPointer===L.Min?this.viewHighValue-t:t-this.viewLowValue,i=M.isNullOrUndefined(this.viewOptions.minRange)?this.viewOptions.step:this.viewOptions.minRange,r=this.viewOptions.maxRange;return or&&(this.currentTrackingPointer===L.Min?(this.viewHighValue=Re.roundToPrecisionLimit(t+r,this.viewOptions.precisionLimit),this.applyViewChange(),this.updateHandles(L.Max,this.valueToPosition(this.viewHighValue))):this.currentTrackingPointer===L.Max&&(this.viewLowValue=Re.roundToPrecisionLimit(t-r,this.viewOptions.precisionLimit),this.applyViewChange(),this.updateHandles(L.Min,this.valueToPosition(this.viewLowValue))),this.updateAriaAttributes()),t}getChangeContext(){const t=new EU;return t.pointerType=this.currentTrackingPointer,t.value=+this.value,this.range&&(t.highValue=+this.highValue),t}static \u0275fac=function(o){return new(o||e)};static \u0275cmp=qt({type:e,selectors:[["ngx-slider"]],contentQueries:function(o,i,r){if(1&o&&Zb(r,sU,5),2&o){let s;wt(s=Et())&&(i.tooltipTemplate=s.first)}},viewQuery:function(o,i){if(1&o&&(Ot(aU,5,so),Ot(lU,5,so),Ot(cU,5,so),Ot(uU,5,so),Ot(dU,5,Hg),Ot(fU,5,Hg),Ot(hU,5,ir),Ot(gU,5,ir),Ot(pU,5,ir),Ot(mU,5,ir),Ot(_U,5,ir),Ot(vU,5,so)),2&o){let r;wt(r=Et())&&(i.leftOuterSelectionBarElement=r.first),wt(r=Et())&&(i.rightOuterSelectionBarElement=r.first),wt(r=Et())&&(i.fullBarElement=r.first),wt(r=Et())&&(i.selectionBarElement=r.first),wt(r=Et())&&(i.minHandleElement=r.first),wt(r=Et())&&(i.maxHandleElement=r.first),wt(r=Et())&&(i.floorLabelElement=r.first),wt(r=Et())&&(i.ceilLabelElement=r.first),wt(r=Et())&&(i.minHandleLabelElement=r.first),wt(r=Et())&&(i.maxHandleLabelElement=r.first),wt(r=Et())&&(i.combinedLabelElement=r.first),wt(r=Et())&&(i.ticksElement=r.first)}},hostVars:10,hostBindings:function(o,i){1&o&&U("resize",function(s){return i.onResize(s)},Ba),2&o&&(ct("disabled",i.sliderElementDisabledAttr)("aria-label",i.sliderElementAriaLabel),An("ngx-slider",i.sliderElementNgxSliderClass)("vertical",i.sliderElementVerticalClass)("animate",i.sliderElementAnimateClass)("with-legend",i.sliderElementWithLegendClass))},inputs:{value:"value",highValue:"highValue",options:"options",manualRefresh:"manualRefresh",triggerFocus:"triggerFocus",cancelUserChange:"cancelUserChange"},outputs:{valueChange:"valueChange",highValueChange:"highValueChange",userChangeStart:"userChangeStart",userChange:"userChange",userChangeEnd:"userChangeEnd"},standalone:!1,features:[Ee([TU]),En],decls:30,vars:12,consts:[["leftOuterSelectionBar",""],["rightOuterSelectionBar",""],["fullBar",""],["selectionBar",""],["minHandle",""],["maxHandle",""],["floorLabel",""],["ceilLabel",""],["minHandleLabel",""],["maxHandleLabel",""],["combinedLabel",""],["ticksElement",""],["ngxSliderElement","",1,"ngx-slider-span","ngx-slider-bar-wrapper","ngx-slider-left-out-selection"],[1,"ngx-slider-span","ngx-slider-bar"],["ngxSliderElement","",1,"ngx-slider-span","ngx-slider-bar-wrapper","ngx-slider-right-out-selection"],["ngxSliderElement","",1,"ngx-slider-span","ngx-slider-bar-wrapper","ngx-slider-full-bar"],["ngxSliderElement","",1,"ngx-slider-span","ngx-slider-bar-wrapper","ngx-slider-selection-bar"],[1,"ngx-slider-span","ngx-slider-bar","ngx-slider-selection",3,"ngStyle"],["ngxSliderHandle","",1,"ngx-slider-span","ngx-slider-pointer","ngx-slider-pointer-min",3,"ngStyle"],["ngxSliderHandle","",1,"ngx-slider-span","ngx-slider-pointer","ngx-slider-pointer-max",3,"ngStyle"],["ngxSliderLabel","",1,"ngx-slider-span","ngx-slider-bubble","ngx-slider-limit","ngx-slider-floor"],["ngxSliderLabel","",1,"ngx-slider-span","ngx-slider-bubble","ngx-slider-limit","ngx-slider-ceil"],["ngxSliderLabel","",1,"ngx-slider-span","ngx-slider-bubble","ngx-slider-model-value"],["ngxSliderLabel","",1,"ngx-slider-span","ngx-slider-bubble","ngx-slider-model-high"],["ngxSliderLabel","",1,"ngx-slider-span","ngx-slider-bubble","ngx-slider-combined"],["ngxSliderElement","",1,"ngx-slider-ticks",3,"hidden"],[1,"ngx-slider-tick",3,"ngClass","ngStyle"],[3,"template","tooltip","placement"],[1,"ngx-slider-span","ngx-slider-tick-value",3,"template","tooltip","placement","content"],[1,"ngx-slider-span","ngx-slider-tick-legend",3,"innerText"],[1,"ngx-slider-span","ngx-slider-tick-legend",3,"innerHTML"]],template:function(o,i){1&o&&(v(0,"span",12,0),O(2,"span",13),_(),v(3,"span",14,1),O(5,"span",13),_(),v(6,"span",15,2),O(8,"span",13),_(),v(9,"span",16,3),O(11,"span",17),_(),O(12,"span",18,4)(14,"span",19,5)(16,"span",20,6)(18,"span",21,7)(20,"span",22,8)(22,"span",23,9)(24,"span",24,10),v(26,"span",25,11),Qe(28,wU,5,10,"span",26,Ye),_()),2&o&&(f(6),An("ngx-slider-transparent",i.fullBarTransparentClass),f(3),An("ngx-slider-draggable",i.selectionBarDraggableClass),f(2),A("ngStyle",i.barStyle),f(),A("ngStyle",i.minPointerStyle),f(2),zl("display",i.range?"inherit":"none"),A("ngStyle",i.maxPointerStyle),f(12),An("ngx-slider-ticks-values-under",i.ticksUnderValuesClass),A("hidden",!i.showTicks),f(2),Ke(i.ticks))},dependencies:[Xi,cE,so,Hg,ir,MU],styles:['.ngx-slider{display:inline-block;position:relative;height:4px;width:100%;margin:35px 0 15px;vertical-align:middle;-webkit-user-select:none;user-select:none;touch-action:pan-y} .ngx-slider.with-legend{margin-bottom:40px} .ngx-slider[disabled]{cursor:not-allowed} .ngx-slider[disabled] .ngx-slider-pointer{cursor:not-allowed;background-color:#d8e0f3} .ngx-slider[disabled] .ngx-slider-draggable{cursor:not-allowed} .ngx-slider[disabled] .ngx-slider-selection{background:#8b91a2} .ngx-slider[disabled] .ngx-slider-tick{cursor:not-allowed} .ngx-slider[disabled] .ngx-slider-tick.ngx-slider-selected{background:#8b91a2} .ngx-slider .ngx-slider-span{white-space:nowrap;position:absolute;display:inline-block} .ngx-slider .ngx-slider-base{width:100%;height:100%;padding:0} .ngx-slider .ngx-slider-bar-wrapper{left:0;box-sizing:border-box;margin-top:-16px;padding-top:16px;width:100%;height:32px;z-index:1} .ngx-slider .ngx-slider-draggable{cursor:move} .ngx-slider .ngx-slider-bar{left:0;width:100%;height:4px;z-index:1;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px} .ngx-slider .ngx-slider-bar-wrapper.ngx-slider-transparent .ngx-slider-bar{background:transparent} .ngx-slider .ngx-slider-bar-wrapper.ngx-slider-left-out-selection .ngx-slider-bar{background:#df002d} .ngx-slider .ngx-slider-bar-wrapper.ngx-slider-right-out-selection .ngx-slider-bar{background:#03a688} .ngx-slider .ngx-slider-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px} .ngx-slider .ngx-slider-pointer{cursor:pointer;width:32px;height:32px;top:-14px;background-color:#0db9f0;z-index:3;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px} .ngx-slider .ngx-slider-pointer:after{content:"";width:8px;height:8px;position:absolute;top:12px;left:12px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;background:#fff} .ngx-slider .ngx-slider-pointer:hover:after{background-color:#fff} .ngx-slider .ngx-slider-pointer.ngx-slider-active{z-index:4} .ngx-slider .ngx-slider-pointer.ngx-slider-active:after{background-color:#451aff} .ngx-slider .ngx-slider-bubble{cursor:default;bottom:16px;padding:1px 3px;color:#55637d;font-size:16px} .ngx-slider .ngx-slider-bubble.ngx-slider-limit{color:#55637d} .ngx-slider .ngx-slider-ticks{box-sizing:border-box;width:100%;height:0;position:absolute;left:0;top:-3px;margin:0;z-index:1;list-style:none} .ngx-slider .ngx-slider-ticks-values-under .ngx-slider-tick-value{top:auto;bottom:-36px} .ngx-slider .ngx-slider-tick{text-align:center;cursor:pointer;width:10px;height:10px;background:#d8e0f3;border-radius:50%;position:absolute;top:0;left:0;margin-left:11px} .ngx-slider .ngx-slider-tick.ngx-slider-selected{background:#0db9f0} .ngx-slider .ngx-slider-tick-value{position:absolute;top:-34px;transform:translate(-50%)} .ngx-slider .ngx-slider-tick-legend{position:absolute;top:24px;transform:translate(-50%);max-width:50px;white-space:normal} .ngx-slider.vertical{position:relative;width:4px;height:100%;margin:0 20px;padding:0;vertical-align:baseline;touch-action:pan-x} .ngx-slider.vertical .ngx-slider-base{width:100%;height:100%;padding:0} .ngx-slider.vertical .ngx-slider-bar-wrapper{top:auto;left:0;margin:0 0 0 -16px;padding:0 0 0 16px;height:100%;width:32px} .ngx-slider.vertical .ngx-slider-bar{bottom:0;left:auto;width:4px;height:100%} .ngx-slider.vertical .ngx-slider-pointer{left:-14px!important;top:auto;bottom:0} .ngx-slider.vertical .ngx-slider-bubble{left:16px!important;bottom:0} .ngx-slider.vertical .ngx-slider-ticks{height:100%;width:0;left:-3px;top:0;z-index:1} .ngx-slider.vertical .ngx-slider-tick{vertical-align:middle;margin-left:auto;margin-top:11px} .ngx-slider.vertical .ngx-slider-tick-value{left:24px;top:auto;transform:translateY(-28%)} .ngx-slider.vertical .ngx-slider-tick-legend{top:auto;right:24px;transform:translateY(-28%);max-width:none;white-space:nowrap} .ngx-slider.vertical .ngx-slider-ticks-values-under .ngx-slider-tick-value{bottom:auto;left:auto;right:24px} .ngx-slider *{transition:none} .ngx-slider.animate .ngx-slider-bar-wrapper{transition:all linear .3s} .ngx-slider.animate .ngx-slider-selection{transition:background-color linear .3s} .ngx-slider.animate .ngx-slider-pointer{transition:all linear .3s} .ngx-slider.animate .ngx-slider-pointer:after{transition:all linear .3s} .ngx-slider.animate .ngx-slider-bubble{transition:all linear .3s} .ngx-slider.animate .ngx-slider-bubble.ngx-slider-limit{transition:opacity linear .3s} .ngx-slider.animate .ngx-slider-bubble.ngx-slider-combined{transition:opacity linear .3s} .ngx-slider.animate .ngx-slider-tick{transition:background-color linear .3s}']})}return e})(),SU=(()=>{class e{static \u0275fac=function(o){return new(o||e)};static \u0275mod=Qn({type:e});static \u0275inj=dn({imports:[hE]})}return e})();class lI{constructor(){this.riskHotspotsSettings=null,this.coverageInfoSettings=null}}class AU{constructor(){this.showLineCoverage=!0,this.showBranchCoverage=!0,this.showMethodCoverage=!0,this.showFullMethodCoverage=!0,this.visibleMetrics=[],this.groupingMaximum=0,this.grouping=0,this.historyComparisionDate="",this.historyComparisionType="",this.filter="",this.lineCoverageMin=0,this.lineCoverageMax=100,this.branchCoverageMin=0,this.branchCoverageMax=100,this.methodCoverageMin=0,this.methodCoverageMax=100,this.methodFullCoverageMin=0,this.methodFullCoverageMax=100,this.sortBy="name",this.sortOrder="asc",this.collapseStates=[]}}class NU{constructor(n){this.et="",this.et=n.et,this.cl=n.cl,this.ucl=n.ucl,this.cal=n.cal,this.tl=n.tl,this.lcq=n.lcq,this.cb=n.cb,this.tb=n.tb,this.bcq=n.bcq,this.cm=n.cm,this.fcm=n.fcm,this.tm=n.tm,this.mcq=n.mcq,this.mfcq=n.mfcq}get coverageRatioText(){return 0===this.tl?"-":this.cl+"/"+this.cal}get branchCoverageRatioText(){return 0===this.tb?"-":this.cb+"/"+this.tb}get methodCoverageRatioText(){return 0===this.tm?"-":this.cm+"/"+this.tm}get methodFullCoverageRatioText(){return 0===this.tm?"-":this.fcm+"/"+this.tm}}class kt{static roundNumber(n){return Math.floor(n*Math.pow(10,kt.maximumDecimalPlacesForCoverageQuotas))/Math.pow(10,kt.maximumDecimalPlacesForCoverageQuotas)}static getNthOrLastIndexOf(n,t,o){let i=0,r=-1,s=-1;for(;i{this.historicCoverages.push(new NU(o))}),this.metrics=n.metrics}get coverage(){return 0===this.coverableLines?NaN:kt.roundNumber(100*this.coveredLines/this.coverableLines)}visible(n){if(""!==n.filter&&-1===this.name.toLowerCase().indexOf(n.filter.toLowerCase()))return!1;let t=this.coverage,o=t;if(t=Number.isNaN(t)?0:t,o=Number.isNaN(o)?100:o,n.lineCoverageMin>t||n.lineCoverageMaxi||n.branchCoverageMaxs||n.methodCoverageMaxl||n.methodFullCoverageMax=this.currentHistoricCoverage.lcq)return!1}else if("branchCoverageIncreaseOnly"===n.historyComparisionType){let u=this.branchCoverage;if(isNaN(u)||u<=this.currentHistoricCoverage.bcq)return!1}else if("branchCoverageDecreaseOnly"===n.historyComparisionType){let u=this.branchCoverage;if(isNaN(u)||u>=this.currentHistoricCoverage.bcq)return!1}else if("methodCoverageIncreaseOnly"===n.historyComparisionType){let u=this.methodCoverage;if(isNaN(u)||u<=this.currentHistoricCoverage.mcq)return!1}else if("methodCoverageDecreaseOnly"===n.historyComparisionType){let u=this.methodCoverage;if(isNaN(u)||u>=this.currentHistoricCoverage.mcq)return!1}else if("fullMethodCoverageIncreaseOnly"===n.historyComparisionType){let u=this.methodFullCoverage;if(isNaN(u)||u<=this.currentHistoricCoverage.mfcq)return!1}else if("fullMethodCoverageDecreaseOnly"===n.historyComparisionType){let u=this.methodFullCoverage;if(isNaN(u)||u>=this.currentHistoricCoverage.mfcq)return!1}return!0}updateCurrentHistoricCoverage(n){if(this.currentHistoricCoverage=null,""!==n)for(let t=0;t-1&&null===t}visible(n){if(""!==n.filter&&this.name.toLowerCase().indexOf(n.filter.toLowerCase())>-1)return!0;for(let t=0;t{var e;class n{get nativeWindow(){return function OU(){return window}()}static#e=e=()=>(this.\u0275fac=function(i){return new(i||n)},this.\u0275prov=X({token:n,factory:n.\u0275fac}))}return e(),n})(),xU=(()=>{var e;class n{constructor(){this.translations={}}static#e=e=()=>(this.\u0275fac=function(i){return new(i||n)},this.\u0275cmp=qt({type:n,selectors:[["pro-button"]],inputs:{translations:"translations"},standalone:!1,decls:3,vars:2,consts:[["href","https://reportgenerator.io/pro","target","_blank",1,"pro-button","pro-button-tiny",3,"title"]],template:function(i,r){1&i&&(D(0,"\xa0"),v(1,"a",0),D(2,"PRO"),_()),2&i&&(f(),A("title",On(r.translations.methodCoverageProVersion)))},encapsulation:2}))}return e(),n})();function RU(e,n){if(1&e){const t=ue();v(0,"div",3)(1,"label")(2,"input",4),Ge("ngModelChange",function(i){B(t);const r=m();return ye(r.showBranchCoverage,i)||(r.showBranchCoverage=i),j(i)}),U("change",function(){B(t);const i=m();return j(i.showBranchCoverageChange.emit(i.showBranchCoverage))}),_(),D(3),_()()}if(2&e){const t=m();f(2),je("ngModel",t.showBranchCoverage),f(),P(" ",t.translations.branchCoverage)}}function kU(e,n){1&e&&O(0,"pro-button",6),2&e&&A("translations",m().translations)}function FU(e,n){1&e&&O(0,"pro-button",6),2&e&&A("translations",m().translations)}function LU(e,n){1&e&&O(0,"pro-button",6),2&e&&A("translations",m(2).translations)}function PU(e,n){1&e&&(v(0,"a",8),O(1,"i",9),_()),2&e&&A("href",m().$implicit.explanationUrl,Wn)}function VU(e,n){if(1&e){const t=ue();v(0,"div",3)(1,"label")(2,"input",7),U("change",function(){const i=B(t).$implicit;return j(m(2).toggleMetric(i))}),_(),D(3),_(),D(4,"\xa0"),y(5,PU,2,1,"a",8),_()}if(2&e){const t=n.$implicit,o=m(2);f(2),A("checked",o.isMetricSelected(t))("disabled",!o.methodCoverageAvailable),f(),P(" ",t.name),f(2),C(t.explanationUrl?5:-1)}}function HU(e,n){if(1&e&&(O(0,"br")(1,"br"),v(2,"b"),D(3),_(),y(4,LU,1,1,"pro-button",6),Qe(5,VU,6,4,"div",3,Ye)),2&e){const t=m();f(3),k(t.translations.metrics),f(),C(t.methodCoverageAvailable?-1:4),f(),Ke(t.metrics)}}let BU=(()=>{var e;class n{constructor(){this.visible=!1,this.visibleChange=new ve,this.translations={},this.branchCoverageAvailable=!1,this.methodCoverageAvailable=!1,this.metrics=[],this.showLineCoverage=!1,this.showLineCoverageChange=new ve,this.showBranchCoverage=!1,this.showBranchCoverageChange=new ve,this.showMethodCoverage=!1,this.showMethodCoverageChange=new ve,this.showMethodFullCoverage=!1,this.showMethodFullCoverageChange=new ve,this.visibleMetrics=[],this.visibleMetricsChange=new ve}isMetricSelected(o){return void 0!==this.visibleMetrics.find(i=>i.name===o.name)}toggleMetric(o){let i=this.visibleMetrics.find(r=>r.name===o.name);i?this.visibleMetrics.splice(this.visibleMetrics.indexOf(i),1):this.visibleMetrics.push(o),this.visibleMetrics=[...this.visibleMetrics],this.visibleMetricsChange.emit(this.visibleMetrics)}close(){this.visible=!1,this.visibleChange.emit(this.visible)}cancelEvent(o){o.stopPropagation()}static#e=e=()=>(this.\u0275fac=function(i){return new(i||n)},this.\u0275cmp=qt({type:n,selectors:[["popup"]],inputs:{visible:"visible",translations:"translations",branchCoverageAvailable:"branchCoverageAvailable",methodCoverageAvailable:"methodCoverageAvailable",metrics:"metrics",showLineCoverage:"showLineCoverage",showBranchCoverage:"showBranchCoverage",showMethodCoverage:"showMethodCoverage",showMethodFullCoverage:"showMethodFullCoverage",visibleMetrics:"visibleMetrics"},outputs:{visibleChange:"visibleChange",showLineCoverageChange:"showLineCoverageChange",showBranchCoverageChange:"showBranchCoverageChange",showMethodCoverageChange:"showMethodCoverageChange",showMethodFullCoverageChange:"showMethodFullCoverageChange",visibleMetricsChange:"visibleMetricsChange"},standalone:!1,decls:22,vars:13,consts:[[1,"popup-container",3,"click"],[1,"popup",3,"click"],[1,"close",3,"click"],[1,"mt-1"],["type","checkbox",3,"ngModelChange","change","ngModel"],["type","checkbox",3,"ngModelChange","change","ngModel","disabled"],[3,"translations"],["type","checkbox",3,"change","checked","disabled"],["target","_blank",3,"href"],[1,"icon-info-circled"]],template:function(i,r){1&i&&(v(0,"div",0),U("click",function(){return r.close()}),v(1,"div",1),U("click",function(a){return r.cancelEvent(a)}),v(2,"div",2),U("click",function(){return r.close()}),D(3,"X"),_(),v(4,"b"),D(5),_(),v(6,"div",3)(7,"label")(8,"input",4),Ge("ngModelChange",function(a){return ye(r.showLineCoverage,a)||(r.showLineCoverage=a),a}),U("change",function(){return r.showLineCoverageChange.emit(r.showLineCoverage)}),_(),D(9),_()(),y(10,RU,4,2,"div",3),v(11,"div",3)(12,"label")(13,"input",5),Ge("ngModelChange",function(a){return ye(r.showMethodCoverage,a)||(r.showMethodCoverage=a),a}),U("change",function(){return r.showMethodCoverageChange.emit(r.showMethodCoverage)}),_(),D(14),_(),y(15,kU,1,1,"pro-button",6),_(),v(16,"div",3)(17,"label")(18,"input",5),Ge("ngModelChange",function(a){return ye(r.showMethodFullCoverage,a)||(r.showMethodFullCoverage=a),a}),U("change",function(){return r.showMethodFullCoverageChange.emit(r.showMethodFullCoverage)}),_(),D(19),_(),y(20,FU,1,1,"pro-button",6),_(),y(21,HU,7,2),_()()),2&i&&(f(5),k(r.translations.coverageTypes),f(3),je("ngModel",r.showLineCoverage),f(),P(" ",r.translations.coverage),f(),C(r.branchCoverageAvailable?10:-1),f(3),je("ngModel",r.showMethodCoverage),A("disabled",!r.methodCoverageAvailable),f(),P(" ",r.translations.methodCoverage),f(),C(r.methodCoverageAvailable?-1:15),f(3),je("ngModel",r.showMethodFullCoverage),A("disabled",!r.methodCoverageAvailable),f(),P(" ",r.translations.fullMethodCoverage),f(),C(r.methodCoverageAvailable?-1:20),f(),C(r.metrics.length>0?21:-1))},dependencies:[gg,vc,ks,xU],encapsulation:2}))}return e(),n})();function jU(e,n){1&e&&O(0,"td",1)}function UU(e,n){1&e&&O(0,"td"),2&e&&jt(Ut("green ",m().greenClass))}function $U(e,n){1&e&&O(0,"td"),2&e&&jt(Ut("red ",m().redClass))}let uI=(()=>{var e;class n{constructor(){this.grayVisible=!0,this.greenVisible=!1,this.redVisible=!1,this.greenClass="",this.redClass="",this._percentage=NaN}get percentage(){return this._percentage}set percentage(o){this._percentage=o,this.grayVisible=isNaN(o),this.greenVisible=!isNaN(o)&&Math.round(o)>0,this.redVisible=!isNaN(o)&&100-Math.round(o)>0,this.greenClass="covered"+Math.round(o),this.redClass="covered"+(100-Math.round(o))}static#e=e=()=>(this.\u0275fac=function(i){return new(i||n)},this.\u0275cmp=qt({type:n,selectors:[["coverage-bar"]],inputs:{percentage:"percentage"},standalone:!1,decls:4,vars:3,consts:[[1,"coverage"],[1,"gray","covered100"],[3,"class"]],template:function(i,r){1&i&&(v(0,"table",0),y(1,jU,1,0,"td",1),y(2,UU,1,3,"td",2),y(3,$U,1,3,"td",2),_()),2&i&&(f(),C(r.grayVisible?1:-1),f(),C(r.greenVisible?2:-1),f(),C(r.redVisible?3:-1))},encapsulation:2,changeDetection:0}))}return e(),n})();const zU=["codeelement-row",""],GU=(e,n)=>({"icon-plus":e,"icon-minus":n});function WU(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.coveredLines)}}function qU(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.uncoveredLines)}}function ZU(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.coverableLines)}}function YU(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.totalLines)}}function QU(e,n){if(1&e&&(v(0,"th",3),D(1),_()),2&e){const t=m();A("title",t.element.coverageRatioText),f(),k(t.element.coveragePercentage)}}function KU(e,n){if(1&e&&(v(0,"th",2),O(1,"coverage-bar",4),_()),2&e){const t=m();f(),A("percentage",t.element.coverage)}}function JU(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.coveredBranches)}}function XU(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.totalBranches)}}function e3(e,n){if(1&e&&(v(0,"th",3),D(1),_()),2&e){const t=m();A("title",t.element.branchCoverageRatioText),f(),k(t.element.branchCoveragePercentage)}}function t3(e,n){if(1&e&&(v(0,"th",2),O(1,"coverage-bar",4),_()),2&e){const t=m();f(),A("percentage",t.element.branchCoverage)}}function n3(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.coveredMethods)}}function o3(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.totalMethods)}}function i3(e,n){if(1&e&&(v(0,"th",3),D(1),_()),2&e){const t=m();A("title",t.element.methodCoverageRatioText),f(),k(t.element.methodCoveragePercentage)}}function r3(e,n){if(1&e&&(v(0,"th",2),O(1,"coverage-bar",4),_()),2&e){const t=m();f(),A("percentage",t.element.methodCoverage)}}function s3(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.fullyCoveredMethods)}}function a3(e,n){if(1&e&&(v(0,"th",2),D(1),_()),2&e){const t=m();f(),k(t.element.totalMethods)}}function l3(e,n){if(1&e&&(v(0,"th",3),D(1),_()),2&e){const t=m();A("title",t.element.methodFullCoverageRatioText),f(),k(t.element.methodFullCoveragePercentage)}}function c3(e,n){if(1&e&&(v(0,"th",2),O(1,"coverage-bar",4),_()),2&e){const t=m();f(),A("percentage",t.element.methodFullCoverage)}}function u3(e,n){1&e&&O(0,"th",2)}let d3=(()=>{var e;class n{constructor(){this.collapsed=!1,this.lineCoverageAvailable=!1,this.branchCoverageAvailable=!1,this.methodCoverageAvailable=!1,this.methodFullCoverageAvailable=!1,this.visibleMetrics=[]}static#e=e=()=>(this.\u0275fac=function(i){return new(i||n)},this.\u0275cmp=qt({type:n,selectors:[["","codeelement-row",""]],inputs:{element:"element",collapsed:"collapsed",lineCoverageAvailable:"lineCoverageAvailable",branchCoverageAvailable:"branchCoverageAvailable",methodCoverageAvailable:"methodCoverageAvailable",methodFullCoverageAvailable:"methodFullCoverageAvailable",visibleMetrics:"visibleMetrics"},standalone:!1,attrs:zU,decls:24,vars:23,consts:[["href","#",3,"click"],[3,"ngClass"],[1,"right"],[1,"right",3,"title"],[3,"percentage"]],template:function(i,r){1&i&&(v(0,"th")(1,"a",0),U("click",function(a){return r.element.toggleCollapse(a)}),O(2,"i",1),D(3),_()(),y(4,WU,2,1,"th",2),y(5,qU,2,1,"th",2),y(6,ZU,2,1,"th",2),y(7,YU,2,1,"th",2),y(8,QU,2,2,"th",3),y(9,KU,2,1,"th",2),y(10,JU,2,1,"th",2),y(11,XU,2,1,"th",2),y(12,e3,2,2,"th",3),y(13,t3,2,1,"th",2),y(14,n3,2,1,"th",2),y(15,o3,2,1,"th",2),y(16,i3,2,2,"th",3),y(17,r3,2,1,"th",2),y(18,s3,2,1,"th",2),y(19,a3,2,1,"th",2),y(20,l3,2,2,"th",3),y(21,c3,2,1,"th",2),Qe(22,u3,1,0,"th",2,Ye)),2&i&&(f(2),A("ngClass",Eh(20,GU,r.element.collapsed,!r.element.collapsed)),f(),P("\n",r.element.name),f(),C(r.lineCoverageAvailable?4:-1),f(),C(r.lineCoverageAvailable?5:-1),f(),C(r.lineCoverageAvailable?6:-1),f(),C(r.lineCoverageAvailable?7:-1),f(),C(r.lineCoverageAvailable?8:-1),f(),C(r.lineCoverageAvailable?9:-1),f(),C(r.branchCoverageAvailable?10:-1),f(),C(r.branchCoverageAvailable?11:-1),f(),C(r.branchCoverageAvailable?12:-1),f(),C(r.branchCoverageAvailable?13:-1),f(),C(r.methodCoverageAvailable?14:-1),f(),C(r.methodCoverageAvailable?15:-1),f(),C(r.methodCoverageAvailable?16:-1),f(),C(r.methodCoverageAvailable?17:-1),f(),C(r.methodFullCoverageAvailable?18:-1),f(),C(r.methodFullCoverageAvailable?19:-1),f(),C(r.methodFullCoverageAvailable?20:-1),f(),C(r.methodFullCoverageAvailable?21:-1),f(),Ke(r.visibleMetrics))},dependencies:[Xi,uI],encapsulation:2,changeDetection:0}))}return e(),n})();const f3=["coverage-history-chart",""];let h3=(()=>{var e;class n{constructor(){this.path=null,this._historicCoverages=[]}get historicCoverages(){return this._historicCoverages}set historicCoverages(o){if(this._historicCoverages=o,o.length>1){let i="";for(let r=0;r(this.\u0275fac=function(i){return new(i||n)},this.\u0275cmp=qt({type:n,selectors:[["","coverage-history-chart",""]],inputs:{historicCoverages:"historicCoverages"},standalone:!1,attrs:f3,decls:3,vars:1,consts:[["width","30","height","18",1,"ct-chart-line"],[1,"ct-series","ct-series-a"],[1,"ct-line"]],template:function(i,r){1&i&&(function tm(){G.lFrame.currentNamespace="svg"}(),v(0,"svg",0)(1,"g",1),O(2,"path",2),_()()),2&i&&(f(2),ct("d",r.path))},encapsulation:2,changeDetection:0}))}return e(),n})();const g3=["class-row",""],Lc=e=>({historiccoverageoffset:e});function p3(e,n){if(1&e&&(v(0,"a",0),D(1),_()),2&e){const t=m();A("href",t.clazz.reportPath,Wn),f(),k(t.clazz.name)}}function m3(e,n){1&e&&D(0),2&e&&P(" ",m().clazz.name," ")}function _3(e,n){if(1&e&&(v(0,"div"),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);jt(Ut("currenthistory ",t.getClassName(t.clazz.coveredLines,t.clazz.currentHistoricCoverage.cl))),f(),P(" ",t.clazz.coveredLines," "),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),P(" ",t.clazz.currentHistoricCoverage.cl," ")}}function v3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.coveredLines," ")}function y3(e,n){if(1&e&&(v(0,"td",1),y(1,_3,4,6),y(2,v3,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function C3(e,n){if(1&e&&(v(0,"div"),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);jt(Ut("currenthistory ",t.getClassName(t.clazz.currentHistoricCoverage.ucl,t.clazz.uncoveredLines))),f(),P(" ",t.clazz.uncoveredLines," "),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),P(" ",t.clazz.currentHistoricCoverage.ucl," ")}}function b3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.uncoveredLines," ")}function D3(e,n){if(1&e&&(v(0,"td",1),y(1,C3,4,6),y(2,b3,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function w3(e,n){if(1&e&&(v(0,"div",4),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);f(),k(t.clazz.coverableLines),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),k(t.clazz.currentHistoricCoverage.cal)}}function E3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.coverableLines," ")}function M3(e,n){if(1&e&&(v(0,"td",1),y(1,w3,4,3),y(2,E3,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function I3(e,n){if(1&e&&(v(0,"div",4),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);f(),k(t.clazz.totalLines),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),k(t.clazz.currentHistoricCoverage.tl)}}function T3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.totalLines," ")}function S3(e,n){if(1&e&&(v(0,"td",1),y(1,I3,4,3),y(2,T3,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function A3(e,n){if(1&e&&O(0,"div",5),2&e){const t=m(2);A("title",On(t.translations.history+": "+t.translations.coverage))("historicCoverages",t.clazz.lineCoverageHistory)("ngClass",Zi(4,Lc,null!==t.clazz.currentHistoricCoverage))}}function N3(e,n){if(1&e&&(v(0,"div"),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);jt(Ut("currenthistory ",t.getClassName(t.clazz.coverage,t.clazz.currentHistoricCoverage.lcq))),f(),P(" ",t.clazz.coveragePercentage," "),f(),A("title",t.clazz.currentHistoricCoverage.et+": "+t.clazz.currentHistoricCoverage.coverageRatioText),f(),P("",t.clazz.currentHistoricCoverage.lcq,"%")}}function O3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.coveragePercentage," ")}function x3(e,n){if(1&e&&(v(0,"td",2),y(1,A3,1,6,"div",5),y(2,N3,4,6),y(3,O3,1,1),_()),2&e){const t=m();A("title",t.clazz.coverageRatioText),f(),C(t.clazz.lineCoverageHistory.length>1?1:-1),f(),C(null!==t.clazz.currentHistoricCoverage?2:-1),f(),C(null===t.clazz.currentHistoricCoverage?3:-1)}}function R3(e,n){if(1&e&&(v(0,"td",1),O(1,"coverage-bar",6),_()),2&e){const t=m();f(),A("percentage",t.clazz.coverage)}}function k3(e,n){if(1&e&&(v(0,"div"),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);jt(Ut("currenthistory ",t.getClassName(t.clazz.coveredBranches,t.clazz.currentHistoricCoverage.cb))),f(),P(" ",t.clazz.coveredBranches," "),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),P(" ",t.clazz.currentHistoricCoverage.cb," ")}}function F3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.coveredBranches," ")}function L3(e,n){if(1&e&&(v(0,"td",1),y(1,k3,4,6),y(2,F3,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function P3(e,n){if(1&e&&(v(0,"div",4),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);f(),k(t.clazz.totalBranches),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),k(t.clazz.currentHistoricCoverage.tb)}}function V3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.totalBranches," ")}function H3(e,n){if(1&e&&(v(0,"td",1),y(1,P3,4,3),y(2,V3,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function B3(e,n){if(1&e&&O(0,"div",7),2&e){const t=m(2);A("title",On(t.translations.history+": "+t.translations.branchCoverage))("historicCoverages",t.clazz.branchCoverageHistory)("ngClass",Zi(4,Lc,null!==t.clazz.currentHistoricCoverage))}}function j3(e,n){if(1&e&&(v(0,"div"),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);jt(Ut("currenthistory ",t.getClassName(t.clazz.branchCoverage,t.clazz.currentHistoricCoverage.bcq))),f(),P(" ",t.clazz.branchCoveragePercentage," "),f(),A("title",t.clazz.currentHistoricCoverage.et+": "+t.clazz.currentHistoricCoverage.branchCoverageRatioText),f(),P("",t.clazz.currentHistoricCoverage.bcq,"%")}}function U3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.branchCoveragePercentage," ")}function $3(e,n){if(1&e&&(v(0,"td",2),y(1,B3,1,6,"div",7),y(2,j3,4,6),y(3,U3,1,1),_()),2&e){const t=m();A("title",t.clazz.branchCoverageRatioText),f(),C(t.clazz.branchCoverageHistory.length>1?1:-1),f(),C(null!==t.clazz.currentHistoricCoverage?2:-1),f(),C(null===t.clazz.currentHistoricCoverage?3:-1)}}function z3(e,n){if(1&e&&(v(0,"td",1),O(1,"coverage-bar",6),_()),2&e){const t=m();f(),A("percentage",t.clazz.branchCoverage)}}function G3(e,n){if(1&e&&(v(0,"div"),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);jt(Ut("currenthistory ",t.getClassName(t.clazz.coveredMethods,t.clazz.currentHistoricCoverage.cm))),f(),P(" ",t.clazz.coveredMethods," "),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),P(" ",t.clazz.currentHistoricCoverage.cm," ")}}function W3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.coveredMethods," ")}function q3(e,n){if(1&e&&(v(0,"td",1),y(1,G3,4,6),y(2,W3,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function Z3(e,n){if(1&e&&(v(0,"div",4),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);f(),k(t.clazz.totalMethods),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),k(t.clazz.currentHistoricCoverage.tm)}}function Y3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.totalMethods," ")}function Q3(e,n){if(1&e&&(v(0,"td",1),y(1,Z3,4,3),y(2,Y3,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function K3(e,n){if(1&e&&O(0,"div",8),2&e){const t=m(2);A("title",On(t.translations.history+": "+t.translations.methodCoverage))("historicCoverages",t.clazz.methodCoverageHistory)("ngClass",Zi(4,Lc,null!==t.clazz.currentHistoricCoverage))}}function J3(e,n){if(1&e&&(v(0,"div"),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);jt(Ut("currenthistory ",t.getClassName(t.clazz.methodCoverage,t.clazz.currentHistoricCoverage.mcq))),f(),P(" ",t.clazz.methodCoveragePercentage," "),f(),A("title",t.clazz.currentHistoricCoverage.et+": "+t.clazz.currentHistoricCoverage.methodCoverageRatioText),f(),P("",t.clazz.currentHistoricCoverage.mcq,"%")}}function X3(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.methodCoveragePercentage," ")}function e$(e,n){if(1&e&&(v(0,"td",2),y(1,K3,1,6,"div",8),y(2,J3,4,6),y(3,X3,1,1),_()),2&e){const t=m();A("title",t.clazz.methodCoverageRatioText),f(),C(t.clazz.methodCoverageHistory.length>1?1:-1),f(),C(null!==t.clazz.currentHistoricCoverage?2:-1),f(),C(null===t.clazz.currentHistoricCoverage?3:-1)}}function t$(e,n){if(1&e&&(v(0,"td",1),O(1,"coverage-bar",6),_()),2&e){const t=m();f(),A("percentage",t.clazz.methodCoverage)}}function n$(e,n){if(1&e&&(v(0,"div"),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);jt(Ut("currenthistory ",t.getClassName(t.clazz.fullyCoveredMethods,t.clazz.currentHistoricCoverage.fcm))),f(),P(" ",t.clazz.fullyCoveredMethods," "),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),P(" ",t.clazz.currentHistoricCoverage.fcm," ")}}function o$(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.fullyCoveredMethods," ")}function i$(e,n){if(1&e&&(v(0,"td",1),y(1,n$,4,6),y(2,o$,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function r$(e,n){if(1&e&&(v(0,"div",4),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);f(),k(t.clazz.totalMethods),f(),A("title",t.clazz.currentHistoricCoverage.et),f(),k(t.clazz.currentHistoricCoverage.tm)}}function s$(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.totalMethods," ")}function a$(e,n){if(1&e&&(v(0,"td",1),y(1,r$,4,3),y(2,s$,1,1),_()),2&e){const t=m();f(),C(null!==t.clazz.currentHistoricCoverage?1:-1),f(),C(null===t.clazz.currentHistoricCoverage?2:-1)}}function l$(e,n){if(1&e&&O(0,"div",9),2&e){const t=m(2);A("title",On(t.translations.history+": "+t.translations.fullMethodCoverage))("historicCoverages",t.clazz.methodFullCoverageHistory)("ngClass",Zi(4,Lc,null!==t.clazz.currentHistoricCoverage))}}function c$(e,n){if(1&e&&(v(0,"div"),D(1),_(),v(2,"div",3),D(3),_()),2&e){const t=m(2);jt(Ut("currenthistory ",t.getClassName(t.clazz.methodFullCoverage,t.clazz.currentHistoricCoverage.mfcq))),f(),P(" ",t.clazz.methodFullCoveragePercentage," "),f(),A("title",t.clazz.currentHistoricCoverage.et+": "+t.clazz.currentHistoricCoverage.methodFullCoverageRatioText),f(),P("",t.clazz.currentHistoricCoverage.mfcq,"%")}}function u$(e,n){1&e&&D(0),2&e&&P(" ",m(2).clazz.methodFullCoveragePercentage," ")}function d$(e,n){if(1&e&&(v(0,"td",2),y(1,l$,1,6,"div",9),y(2,c$,4,6),y(3,u$,1,1),_()),2&e){const t=m();A("title",t.clazz.methodFullCoverageRatioText),f(),C(t.clazz.methodFullCoverageHistory.length>1?1:-1),f(),C(null!==t.clazz.currentHistoricCoverage?2:-1),f(),C(null===t.clazz.currentHistoricCoverage?3:-1)}}function f$(e,n){if(1&e&&(v(0,"td",1),O(1,"coverage-bar",6),_()),2&e){const t=m();f(),A("percentage",t.clazz.methodFullCoverage)}}function h$(e,n){if(1&e&&(v(0,"td",1),D(1),_()),2&e){const t=n.$implicit,o=m();f(),k(o.clazz.metrics[t.abbreviation])}}let g$=(()=>{var e;class n{constructor(){this.translations={},this.lineCoverageAvailable=!1,this.branchCoverageAvailable=!1,this.methodCoverageAvailable=!1,this.methodFullCoverageAvailable=!1,this.visibleMetrics=[],this.historyComparisionDate=""}getClassName(o,i){return o>i?"lightgreen":o(this.\u0275fac=function(i){return new(i||n)},this.\u0275cmp=qt({type:n,selectors:[["","class-row",""]],inputs:{clazz:"clazz",translations:"translations",lineCoverageAvailable:"lineCoverageAvailable",branchCoverageAvailable:"branchCoverageAvailable",methodCoverageAvailable:"methodCoverageAvailable",methodFullCoverageAvailable:"methodFullCoverageAvailable",visibleMetrics:"visibleMetrics",historyComparisionDate:"historyComparisionDate"},standalone:!1,attrs:g3,decls:23,vars:20,consts:[[3,"href"],[1,"right"],[1,"right",3,"title"],[3,"title"],[1,"currenthistory"],["coverage-history-chart","",1,"tinylinecoveragechart","ct-chart",3,"historicCoverages","ngClass","title"],[3,"percentage"],["coverage-history-chart","",1,"tinybranchcoveragechart","ct-chart",3,"historicCoverages","ngClass","title"],["coverage-history-chart","",1,"tinymethodcoveragechart","ct-chart",3,"historicCoverages","ngClass","title"],["coverage-history-chart","",1,"tinyfullmethodcoveragechart","ct-chart",3,"historicCoverages","ngClass","title"]],template:function(i,r){1&i&&(v(0,"td"),y(1,p3,2,2,"a",0),y(2,m3,1,1),_(),y(3,y3,3,2,"td",1),y(4,D3,3,2,"td",1),y(5,M3,3,2,"td",1),y(6,S3,3,2,"td",1),y(7,x3,4,4,"td",2),y(8,R3,2,1,"td",1),y(9,L3,3,2,"td",1),y(10,H3,3,2,"td",1),y(11,$3,4,4,"td",2),y(12,z3,2,1,"td",1),y(13,q3,3,2,"td",1),y(14,Q3,3,2,"td",1),y(15,e$,4,4,"td",2),y(16,t$,2,1,"td",1),y(17,i$,3,2,"td",1),y(18,a$,3,2,"td",1),y(19,d$,4,4,"td",2),y(20,f$,2,1,"td",1),Qe(21,h$,2,1,"td",1,Ye)),2&i&&(f(),C(""!==r.clazz.reportPath?1:-1),f(),C(""===r.clazz.reportPath?2:-1),f(),C(r.lineCoverageAvailable?3:-1),f(),C(r.lineCoverageAvailable?4:-1),f(),C(r.lineCoverageAvailable?5:-1),f(),C(r.lineCoverageAvailable?6:-1),f(),C(r.lineCoverageAvailable?7:-1),f(),C(r.lineCoverageAvailable?8:-1),f(),C(r.branchCoverageAvailable?9:-1),f(),C(r.branchCoverageAvailable?10:-1),f(),C(r.branchCoverageAvailable?11:-1),f(),C(r.branchCoverageAvailable?12:-1),f(),C(r.methodCoverageAvailable?13:-1),f(),C(r.methodCoverageAvailable?14:-1),f(),C(r.methodCoverageAvailable?15:-1),f(),C(r.methodCoverageAvailable?16:-1),f(),C(r.methodFullCoverageAvailable?17:-1),f(),C(r.methodFullCoverageAvailable?18:-1),f(),C(r.methodFullCoverageAvailable?19:-1),f(),C(r.methodFullCoverageAvailable?20:-1),f(),Ke(r.visibleMetrics))},dependencies:[Xi,h3,uI],encapsulation:2,changeDetection:0}))}return e(),n})();const it=(e,n,t)=>({"icon-up-dir_active":e,"icon-down-dir_active":n,"icon-up-down-dir":t});function p$(e,n){if(1&e){const t=ue();v(0,"popup",27),Ge("visibleChange",function(i){B(t);const r=m(2);return ye(r.popupVisible,i)||(r.popupVisible=i),j(i)})("showLineCoverageChange",function(i){B(t);const r=m(2);return ye(r.settings.showLineCoverage,i)||(r.settings.showLineCoverage=i),j(i)})("showBranchCoverageChange",function(i){B(t);const r=m(2);return ye(r.settings.showBranchCoverage,i)||(r.settings.showBranchCoverage=i),j(i)})("showMethodCoverageChange",function(i){B(t);const r=m(2);return ye(r.settings.showMethodCoverage,i)||(r.settings.showMethodCoverage=i),j(i)})("showMethodFullCoverageChange",function(i){B(t);const r=m(2);return ye(r.settings.showFullMethodCoverage,i)||(r.settings.showFullMethodCoverage=i),j(i)})("visibleMetricsChange",function(i){B(t);const r=m(2);return ye(r.settings.visibleMetrics,i)||(r.settings.visibleMetrics=i),j(i)}),_()}if(2&e){const t=m(2);je("visible",t.popupVisible),A("translations",t.translations)("branchCoverageAvailable",t.branchCoverageAvailable)("methodCoverageAvailable",t.methodCoverageAvailable)("metrics",t.metrics),je("showLineCoverage",t.settings.showLineCoverage)("showBranchCoverage",t.settings.showBranchCoverage)("showMethodCoverage",t.settings.showMethodCoverage)("showMethodFullCoverage",t.settings.showFullMethodCoverage)("visibleMetrics",t.settings.visibleMetrics)}}function m$(e,n){1&e&&D(0),2&e&&P(" ",m(2).translations.noGrouping," ")}function _$(e,n){1&e&&D(0),2&e&&P(" ",m(2).translations.byAssembly," ")}function v$(e,n){if(1&e&&D(0),2&e){const t=m(2);P(" ",t.translations.byNamespace+" "+t.settings.grouping," ")}}function y$(e,n){if(1&e&&(v(0,"option",30),D(1),_()),2&e){const t=n.$implicit;A("value",t),f(),k(t)}}function C$(e,n){1&e&&O(0,"br")}function b$(e,n){if(1&e&&(v(0,"option",34),D(1),_()),2&e){const t=m(4);f(),P(" ",t.translations.branchCoverageIncreaseOnly," ")}}function D$(e,n){if(1&e&&(v(0,"option",35),D(1),_()),2&e){const t=m(4);f(),P(" ",t.translations.branchCoverageDecreaseOnly," ")}}function w$(e,n){if(1&e&&(v(0,"option",36),D(1),_()),2&e){const t=m(4);f(),P(" ",t.translations.methodCoverageIncreaseOnly," ")}}function E$(e,n){if(1&e&&(v(0,"option",37),D(1),_()),2&e){const t=m(4);f(),P(" ",t.translations.methodCoverageDecreaseOnly," ")}}function M$(e,n){if(1&e&&(v(0,"option",38),D(1),_()),2&e){const t=m(4);f(),P(" ",t.translations.fullMethodCoverageIncreaseOnly," ")}}function I$(e,n){if(1&e&&(v(0,"option",39),D(1),_()),2&e){const t=m(4);f(),P(" ",t.translations.fullMethodCoverageDecreaseOnly," ")}}function T$(e,n){if(1&e){const t=ue();v(0,"div")(1,"select",28),Ge("ngModelChange",function(i){B(t);const r=m(3);return ye(r.settings.historyComparisionType,i)||(r.settings.historyComparisionType=i),j(i)}),v(2,"option",29),D(3),_(),v(4,"option",31),D(5),_(),v(6,"option",32),D(7),_(),v(8,"option",33),D(9),_(),y(10,b$,2,1,"option",34),y(11,D$,2,1,"option",35),y(12,w$,2,1,"option",36),y(13,E$,2,1,"option",37),y(14,M$,2,1,"option",38),y(15,I$,2,1,"option",39),_()()}if(2&e){const t=m(3);f(),je("ngModel",t.settings.historyComparisionType),f(2),k(t.translations.filter),f(2),k(t.translations.allChanges),f(2),k(t.translations.lineCoverageIncreaseOnly),f(2),k(t.translations.lineCoverageDecreaseOnly),f(),C(t.branchCoverageAvailable?10:-1),f(),C(t.branchCoverageAvailable?11:-1),f(),C(t.methodCoverageAvailable?12:-1),f(),C(t.methodCoverageAvailable?13:-1),f(),C(t.methodCoverageAvailable?14:-1),f(),C(t.methodCoverageAvailable?15:-1)}}function S$(e,n){if(1&e){const t=ue();v(0,"div"),D(1),v(2,"select",28),Ge("ngModelChange",function(i){B(t);const r=m(2);return ye(r.settings.historyComparisionDate,i)||(r.settings.historyComparisionDate=i),j(i)}),U("ngModelChange",function(){return B(t),j(m(2).updateCurrentHistoricCoverage())}),v(3,"option",29),D(4),_(),Qe(5,y$,2,2,"option",30,Ye),_()(),y(7,C$,1,0,"br"),y(8,T$,16,11,"div")}if(2&e){const t=m(2);f(),P(" ",t.translations.compareHistory," "),f(),je("ngModel",t.settings.historyComparisionDate),f(2),k(t.translations.date),f(),Ke(t.historicCoverageExecutionTimes),f(2),C(""!==t.settings.historyComparisionDate?7:-1),f(),C(""!==t.settings.historyComparisionDate?8:-1)}}function A$(e,n){1&e&&O(0,"col",12)}function N$(e,n){1&e&&O(0,"col",13)}function O$(e,n){1&e&&O(0,"col",14)}function x$(e,n){1&e&&O(0,"col",15)}function R$(e,n){1&e&&O(0,"col",16)}function k$(e,n){1&e&&O(0,"col",17)}function F$(e,n){1&e&&O(0,"col",12)}function L$(e,n){1&e&&O(0,"col",15)}function P$(e,n){1&e&&O(0,"col",16)}function V$(e,n){1&e&&O(0,"col",17)}function H$(e,n){1&e&&O(0,"col",12)}function B$(e,n){1&e&&O(0,"col",15)}function j$(e,n){1&e&&O(0,"col",16)}function U$(e,n){1&e&&O(0,"col",17)}function $$(e,n){1&e&&O(0,"col",12)}function z$(e,n){1&e&&O(0,"col",15)}function G$(e,n){1&e&&O(0,"col",16)}function W$(e,n){1&e&&O(0,"col",17)}function q$(e,n){1&e&&O(0,"col",17)}function Z$(e,n){if(1&e&&(v(0,"th",19),D(1),_()),2&e){const t=m(2);f(),k(t.translations.coverage)}}function Y$(e,n){if(1&e&&(v(0,"th",20),D(1),_()),2&e){const t=m(2);f(),k(t.translations.branchCoverage)}}function Q$(e,n){if(1&e&&(v(0,"th",20),D(1),_()),2&e){const t=m(2);f(),k(t.translations.methodCoverage)}}function K$(e,n){if(1&e&&(v(0,"th",20),D(1),_()),2&e){const t=m(2);f(),k(t.translations.fullMethodCoverage)}}function J$(e,n){if(1&e&&(v(0,"th",21),D(1),_()),2&e){const t=m(2);ct("colspan",t.settings.visibleMetrics.length),f(),k(t.translations.metrics)}}function X$(e,n){if(1&e){const t=ue();v(0,"td",19)(1,"ngx-slider",40),Ge("valueChange",function(i){B(t);const r=m(2);return ye(r.settings.lineCoverageMin,i)||(r.settings.lineCoverageMin=i),j(i)})("highValueChange",function(i){B(t);const r=m(2);return ye(r.settings.lineCoverageMax,i)||(r.settings.lineCoverageMax=i),j(i)}),_()()}if(2&e){const t=m(2);f(),je("value",t.settings.lineCoverageMin)("highValue",t.settings.lineCoverageMax),A("options",t.sliderOptions)}}function e8(e,n){if(1&e){const t=ue();v(0,"td",20)(1,"ngx-slider",40),Ge("valueChange",function(i){B(t);const r=m(2);return ye(r.settings.branchCoverageMin,i)||(r.settings.branchCoverageMin=i),j(i)})("highValueChange",function(i){B(t);const r=m(2);return ye(r.settings.branchCoverageMax,i)||(r.settings.branchCoverageMax=i),j(i)}),_()()}if(2&e){const t=m(2);f(),je("value",t.settings.branchCoverageMin)("highValue",t.settings.branchCoverageMax),A("options",t.sliderOptions)}}function t8(e,n){if(1&e){const t=ue();v(0,"td",20)(1,"ngx-slider",40),Ge("valueChange",function(i){B(t);const r=m(2);return ye(r.settings.methodCoverageMin,i)||(r.settings.methodCoverageMin=i),j(i)})("highValueChange",function(i){B(t);const r=m(2);return ye(r.settings.methodCoverageMax,i)||(r.settings.methodCoverageMax=i),j(i)}),_()()}if(2&e){const t=m(2);f(),je("value",t.settings.methodCoverageMin)("highValue",t.settings.methodCoverageMax),A("options",t.sliderOptions)}}function n8(e,n){if(1&e){const t=ue();v(0,"td",20)(1,"ngx-slider",40),Ge("valueChange",function(i){B(t);const r=m(2);return ye(r.settings.methodFullCoverageMin,i)||(r.settings.methodFullCoverageMin=i),j(i)})("highValueChange",function(i){B(t);const r=m(2);return ye(r.settings.methodFullCoverageMax,i)||(r.settings.methodFullCoverageMax=i),j(i)}),_()()}if(2&e){const t=m(2);f(),je("value",t.settings.methodFullCoverageMin)("highValue",t.settings.methodFullCoverageMax),A("options",t.sliderOptions)}}function o8(e,n){1&e&&O(0,"td",21),2&e&&ct("colspan",m(2).settings.visibleMetrics.length)}function i8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("covered",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"covered"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"covered"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"covered"!==t.settings.sortBy)),f(),k(t.translations.covered)}}function r8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("uncovered",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"uncovered"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"uncovered"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"uncovered"!==t.settings.sortBy)),f(),k(t.translations.uncovered)}}function s8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("coverable",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"coverable"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"coverable"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"coverable"!==t.settings.sortBy)),f(),k(t.translations.coverable)}}function a8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("total",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"total"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"total"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"total"!==t.settings.sortBy)),f(),k(t.translations.total)}}function l8(e,n){if(1&e){const t=ue();v(0,"th",26)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("coverage",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"coverage"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"coverage"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"coverage"!==t.settings.sortBy)),f(),k(t.translations.percentage)}}function c8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("covered_branches",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"covered_branches"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"covered_branches"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"covered_branches"!==t.settings.sortBy)),f(),k(t.translations.covered)}}function u8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("total_branches",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"total_branches"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"total_branches"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"total_branches"!==t.settings.sortBy)),f(),k(t.translations.total)}}function d8(e,n){if(1&e){const t=ue();v(0,"th",26)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("branchcoverage",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"branchcoverage"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"branchcoverage"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"branchcoverage"!==t.settings.sortBy)),f(),k(t.translations.percentage)}}function f8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("covered_methods",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"covered_methods"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"covered_methods"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"covered_methods"!==t.settings.sortBy)),f(),k(t.translations.covered)}}function h8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("total_methods",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"total_methods"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"total_methods"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"total_methods"!==t.settings.sortBy)),f(),k(t.translations.total)}}function g8(e,n){if(1&e){const t=ue();v(0,"th",26)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("methodcoverage",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"methodcoverage"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"methodcoverage"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"methodcoverage"!==t.settings.sortBy)),f(),k(t.translations.percentage)}}function p8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("fullycovered_methods",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"fullycovered_methods"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"fullycovered_methods"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"fullycovered_methods"!==t.settings.sortBy)),f(),k(t.translations.covered)}}function m8(e,n){if(1&e){const t=ue();v(0,"th",25)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("total_methods",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"total_methods"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"total_methods"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"total_methods"!==t.settings.sortBy)),f(),k(t.translations.total)}}function _8(e,n){if(1&e){const t=ue();v(0,"th",26)(1,"a",2),U("click",function(i){return B(t),j(m(2).updateSorting("methodfullcoverage",i))}),O(2,"i",24),D(3),_()()}if(2&e){const t=m(2);f(2),A("ngClass",Ne(2,it,"methodfullcoverage"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"methodfullcoverage"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"methodfullcoverage"!==t.settings.sortBy)),f(),k(t.translations.percentage)}}function v8(e,n){if(1&e){const t=ue();v(0,"th")(1,"a",2),U("click",function(i){const r=B(t).$implicit;return j(m(2).updateSorting(r.abbreviation,i))}),O(2,"i",24),D(3),_(),v(4,"a",41),O(5,"i",42),_()()}if(2&e){const t=n.$implicit,o=m(2);f(2),A("ngClass",Ne(4,it,o.settings.sortBy===t.abbreviation&&"asc"===o.settings.sortOrder,o.settings.sortBy===t.abbreviation&&"desc"===o.settings.sortOrder,o.settings.sortBy!==t.abbreviation)),f(),k(t.name),f(),A("href",On(t.explanationUrl),Wn)}}function y8(e,n){if(1&e&&O(0,"tr",43),2&e){const t=m().$implicit,o=m(2);A("element",t)("collapsed",t.collapsed)("lineCoverageAvailable",o.settings.showLineCoverage)("branchCoverageAvailable",o.branchCoverageAvailable&&o.settings.showBranchCoverage)("methodCoverageAvailable",o.methodCoverageAvailable&&o.settings.showMethodCoverage)("methodFullCoverageAvailable",o.methodCoverageAvailable&&o.settings.showFullMethodCoverage)("visibleMetrics",o.settings.visibleMetrics)}}function C8(e,n){if(1&e&&O(0,"tr",44),2&e){const t=m().$implicit,o=m(3);A("clazz",t)("translations",o.translations)("lineCoverageAvailable",o.settings.showLineCoverage)("branchCoverageAvailable",o.branchCoverageAvailable&&o.settings.showBranchCoverage)("methodCoverageAvailable",o.methodCoverageAvailable&&o.settings.showMethodCoverage)("methodFullCoverageAvailable",o.methodCoverageAvailable&&o.settings.showFullMethodCoverage)("visibleMetrics",o.settings.visibleMetrics)("historyComparisionDate",o.settings.historyComparisionDate)}}function b8(e,n){if(1&e&&y(0,C8,1,8,"tr",44),2&e){const t=n.$implicit,o=m().$implicit,i=m(2);C(!o.collapsed&&t.visible(i.settings)?0:-1)}}function D8(e,n){if(1&e&&O(0,"tr",46),2&e){const t=m().$implicit,o=m(5);A("clazz",t)("translations",o.translations)("lineCoverageAvailable",o.settings.showLineCoverage)("branchCoverageAvailable",o.branchCoverageAvailable&&o.settings.showBranchCoverage)("methodCoverageAvailable",o.methodCoverageAvailable&&o.settings.showMethodCoverage)("methodFullCoverageAvailable",o.methodCoverageAvailable&&o.settings.showFullMethodCoverage)("visibleMetrics",o.settings.visibleMetrics)("historyComparisionDate",o.settings.historyComparisionDate)}}function w8(e,n){if(1&e&&y(0,D8,1,8,"tr",46),2&e){const t=n.$implicit,o=m(2).$implicit,i=m(3);C(!o.collapsed&&t.visible(i.settings)?0:-1)}}function E8(e,n){if(1&e&&(O(0,"tr",45),Qe(1,w8,1,1,null,null,Ye)),2&e){const t=m().$implicit,o=m(3);A("element",t)("collapsed",t.collapsed)("lineCoverageAvailable",o.settings.showLineCoverage)("branchCoverageAvailable",o.branchCoverageAvailable&&o.settings.showBranchCoverage)("methodCoverageAvailable",o.methodCoverageAvailable&&o.settings.showMethodCoverage)("methodFullCoverageAvailable",o.methodCoverageAvailable&&o.settings.showFullMethodCoverage)("visibleMetrics",o.settings.visibleMetrics),f(),Ke(t.classes)}}function M8(e,n){if(1&e&&y(0,E8,3,7),2&e){const t=n.$implicit,o=m().$implicit,i=m(2);C(!o.collapsed&&t.visible(i.settings)?0:-1)}}function I8(e,n){if(1&e&&(y(0,y8,1,7,"tr",43),Qe(1,b8,1,1,null,null,Ye),Qe(3,M8,1,1,null,null,Ye)),2&e){const t=n.$implicit,o=m(2);C(t.visible(o.settings)?0:-1),f(),Ke(t.classes),f(2),Ke(t.subElements)}}function T8(e,n){if(1&e){const t=ue();v(0,"div"),y(1,p$,1,10,"popup",0),v(2,"div",1)(3,"div")(4,"a",2),U("click",function(i){return B(t),j(m().collapseAll(i))}),D(5),_(),D(6," | "),v(7,"a",2),U("click",function(i){return B(t),j(m().expandAll(i))}),D(8),_()(),v(9,"div",3)(10,"span",4),y(11,m$,1,1),y(12,_$,1,1),y(13,v$,1,1),_(),O(14,"br"),D(15),v(16,"input",5),Ge("ngModelChange",function(i){B(t);const r=m();return ye(r.settings.grouping,i)||(r.settings.grouping=i),j(i)}),U("ngModelChange",function(){return B(t),j(m().updateCoverageInfo())}),_()(),v(17,"div",3),y(18,S$,9,5),_(),v(19,"div",6)(20,"button",7),U("click",function(){return B(t),j(m().popupVisible=!0)}),O(21,"i",8),D(22),_()()(),v(23,"div",9)(24,"table",10)(25,"colgroup"),O(26,"col",11),y(27,A$,1,0,"col",12),y(28,N$,1,0,"col",13),y(29,O$,1,0,"col",14),y(30,x$,1,0,"col",15),y(31,R$,1,0,"col",16),y(32,k$,1,0,"col",17),y(33,F$,1,0,"col",12),y(34,L$,1,0,"col",15),y(35,P$,1,0,"col",16),y(36,V$,1,0,"col",17),y(37,H$,1,0,"col",12),y(38,B$,1,0,"col",15),y(39,j$,1,0,"col",16),y(40,U$,1,0,"col",17),y(41,$$,1,0,"col",12),y(42,z$,1,0,"col",15),y(43,G$,1,0,"col",16),y(44,W$,1,0,"col",17),Qe(45,q$,1,0,"col",17,Ye),_(),v(47,"thead")(48,"tr",18),O(49,"th"),y(50,Z$,2,1,"th",19),y(51,Y$,2,1,"th",20),y(52,Q$,2,1,"th",20),y(53,K$,2,1,"th",20),y(54,J$,2,2,"th",21),_(),v(55,"tr",22)(56,"td")(57,"input",23),Ge("ngModelChange",function(i){B(t);const r=m();return ye(r.settings.filter,i)||(r.settings.filter=i),j(i)}),_()(),y(58,X$,2,3,"td",19),y(59,e8,2,3,"td",20),y(60,t8,2,3,"td",20),y(61,n8,2,3,"td",20),y(62,o8,1,1,"td",21),_(),v(63,"tr")(64,"th")(65,"a",2),U("click",function(i){return B(t),j(m().updateSorting("name",i))}),O(66,"i",24),D(67),_()(),y(68,i8,4,6,"th",25),y(69,r8,4,6,"th",25),y(70,s8,4,6,"th",25),y(71,a8,4,6,"th",25),y(72,l8,4,6,"th",26),y(73,c8,4,6,"th",25),y(74,u8,4,6,"th",25),y(75,d8,4,6,"th",26),y(76,f8,4,6,"th",25),y(77,h8,4,6,"th",25),y(78,g8,4,6,"th",26),y(79,p8,4,6,"th",25),y(80,m8,4,6,"th",25),y(81,_8,4,6,"th",26),Qe(82,v8,6,8,"th",null,Ye),_()(),v(84,"tbody"),Qe(85,I8,5,1,null,null,Ye),_()()()()}if(2&e){const t=m();f(),C(t.popupVisible?1:-1),f(4),k(t.translations.collapseAll),f(3),k(t.translations.expandAll),f(3),C(-1===t.settings.grouping?11:-1),f(),C(0===t.settings.grouping?12:-1),f(),C(t.settings.grouping>0?13:-1),f(2),P(" ",t.translations.grouping," "),f(),A("max",t.settings.groupingMaximum),je("ngModel",t.settings.grouping),f(2),C(t.historicCoverageExecutionTimes.length>0?18:-1),f(4),k(t.metrics.length>0?t.translations.selectCoverageTypesAndMetrics:t.translations.selectCoverageTypes),f(5),C(t.settings.showLineCoverage?27:-1),f(),C(t.settings.showLineCoverage?28:-1),f(),C(t.settings.showLineCoverage?29:-1),f(),C(t.settings.showLineCoverage?30:-1),f(),C(t.settings.showLineCoverage?31:-1),f(),C(t.settings.showLineCoverage?32:-1),f(),C(t.branchCoverageAvailable&&t.settings.showBranchCoverage?33:-1),f(),C(t.branchCoverageAvailable&&t.settings.showBranchCoverage?34:-1),f(),C(t.branchCoverageAvailable&&t.settings.showBranchCoverage?35:-1),f(),C(t.branchCoverageAvailable&&t.settings.showBranchCoverage?36:-1),f(),C(t.methodCoverageAvailable&&t.settings.showMethodCoverage?37:-1),f(),C(t.methodCoverageAvailable&&t.settings.showMethodCoverage?38:-1),f(),C(t.methodCoverageAvailable&&t.settings.showMethodCoverage?39:-1),f(),C(t.methodCoverageAvailable&&t.settings.showMethodCoverage?40:-1),f(),C(t.methodCoverageAvailable&&t.settings.showFullMethodCoverage?41:-1),f(),C(t.methodCoverageAvailable&&t.settings.showFullMethodCoverage?42:-1),f(),C(t.methodCoverageAvailable&&t.settings.showFullMethodCoverage?43:-1),f(),C(t.methodCoverageAvailable&&t.settings.showFullMethodCoverage?44:-1),f(),Ke(t.settings.visibleMetrics),f(5),C(t.settings.showLineCoverage?50:-1),f(),C(t.branchCoverageAvailable&&t.settings.showBranchCoverage?51:-1),f(),C(t.methodCoverageAvailable&&t.settings.showMethodCoverage?52:-1),f(),C(t.methodCoverageAvailable&&t.settings.showFullMethodCoverage?53:-1),f(),C(t.settings.visibleMetrics.length>0?54:-1),f(3),A("placeholder",On(t.translations.filter)),je("ngModel",t.settings.filter),f(),C(t.settings.showLineCoverage?58:-1),f(),C(t.branchCoverageAvailable&&t.settings.showBranchCoverage?59:-1),f(),C(t.methodCoverageAvailable&&t.settings.showMethodCoverage?60:-1),f(),C(t.methodCoverageAvailable&&t.settings.showFullMethodCoverage?61:-1),f(),C(t.settings.visibleMetrics.length>0?62:-1),f(4),A("ngClass",Ne(58,it,"name"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"name"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"name"!==t.settings.sortBy)),f(),k(t.translations.name),f(),C(t.settings.showLineCoverage?68:-1),f(),C(t.settings.showLineCoverage?69:-1),f(),C(t.settings.showLineCoverage?70:-1),f(),C(t.settings.showLineCoverage?71:-1),f(),C(t.settings.showLineCoverage?72:-1),f(),C(t.branchCoverageAvailable&&t.settings.showBranchCoverage?73:-1),f(),C(t.branchCoverageAvailable&&t.settings.showBranchCoverage?74:-1),f(),C(t.branchCoverageAvailable&&t.settings.showBranchCoverage?75:-1),f(),C(t.methodCoverageAvailable&&t.settings.showMethodCoverage?76:-1),f(),C(t.methodCoverageAvailable&&t.settings.showMethodCoverage?77:-1),f(),C(t.methodCoverageAvailable&&t.settings.showMethodCoverage?78:-1),f(),C(t.methodCoverageAvailable&&t.settings.showFullMethodCoverage?79:-1),f(),C(t.methodCoverageAvailable&&t.settings.showFullMethodCoverage?80:-1),f(),C(t.methodCoverageAvailable&&t.settings.showFullMethodCoverage?81:-1),f(),Ke(t.settings.visibleMetrics),f(3),Ke(t.codeElements)}}let S8=(()=>{var e;class n{constructor(o){this.queryString="",this.historicCoverageExecutionTimes=[],this.branchCoverageAvailable=!1,this.methodCoverageAvailable=!1,this.metrics=[],this.codeElements=[],this.translations={},this.popupVisible=!1,this.settings=new AU,this.sliderOptions={floor:0,ceil:100,step:1,ticksArray:[0,10,20,30,40,50,60,70,80,90,100],showTicks:!0},this.window=o.nativeWindow}ngOnInit(){this.historicCoverageExecutionTimes=this.window.historicCoverageExecutionTimes,this.branchCoverageAvailable=this.window.branchCoverageAvailable,this.methodCoverageAvailable=this.window.methodCoverageAvailable,this.metrics=this.window.metrics,this.translations=this.window.translations,kt.maximumDecimalPlacesForCoverageQuotas=this.window.maximumDecimalPlacesForCoverageQuotas;let o=!1;if(void 0!==this.window.history&&void 0!==this.window.history.replaceState&&null!==this.window.history.state&&null!=this.window.history.state.coverageInfoSettings)console.log("Coverage info: Restoring from history",this.window.history.state.coverageInfoSettings),o=!0,this.settings=JSON.parse(JSON.stringify(this.window.history.state.coverageInfoSettings));else{let r=0,s=this.window.assemblies;for(let a=0;a-1&&(this.queryString=window.location.href.substring(i)),this.updateCoverageInfo(),o&&this.restoreCollapseState()}onBeforeUnload(){if(this.saveCollapseState(),void 0!==this.window.history&&void 0!==this.window.history.replaceState){console.log("Coverage info: Updating history",this.settings);let o=new lI;null!==window.history.state&&(o=JSON.parse(JSON.stringify(this.window.history.state))),o.coverageInfoSettings=JSON.parse(JSON.stringify(this.settings)),window.history.replaceState(o,"")}}updateCoverageInfo(){let o=(new Date).getTime(),i=this.window.assemblies,r=[],s=0;if(0===this.settings.grouping)for(let c=0;c{for(let r=0;r{for(let s=0;so&&(r[s].collapsed=this.settings.collapseStates[o]),o++,i(r[s].subElements)};i(this.codeElements)}static#e=e=()=>(this.\u0275fac=function(i){return new(i||n)(x(jg))},this.\u0275cmp=qt({type:n,selectors:[["coverage-info"]],hostBindings:function(i,r){1&i&&U("beforeunload",function(){return r.onBeforeUnload()},Ba)},standalone:!1,decls:1,vars:1,consts:[[3,"visible","translations","branchCoverageAvailable","methodCoverageAvailable","metrics","showLineCoverage","showBranchCoverage","showMethodCoverage","showMethodFullCoverage","visibleMetrics"],[1,"customizebox"],["href","#",3,"click"],[1,"col-center"],[1,"slider-label"],["type","range","step","1","min","-1",3,"ngModelChange","max","ngModel"],[1,"col-right","right"],["type","button",3,"click"],[1,"icon-cog"],[1,"table-responsive"],[1,"overview","table-fixed","stripped"],[1,"column-min-200"],[1,"column90"],[1,"column105"],[1,"column100"],[1,"column70"],[1,"column98"],[1,"column112"],[1,"header"],["colspan","6",1,"center"],["colspan","4",1,"center"],[1,"center"],[1,"filterbar"],["type","search",3,"ngModelChange","ngModel","placeholder"],[3,"ngClass"],[1,"right"],["colspan","2",1,"center"],[3,"visibleChange","showLineCoverageChange","showBranchCoverageChange","showMethodCoverageChange","showMethodFullCoverageChange","visibleMetricsChange","visible","translations","branchCoverageAvailable","methodCoverageAvailable","metrics","showLineCoverage","showBranchCoverage","showMethodCoverage","showMethodFullCoverage","visibleMetrics"],[3,"ngModelChange","ngModel"],["value",""],[3,"value"],["value","allChanges"],["value","lineCoverageIncreaseOnly"],["value","lineCoverageDecreaseOnly"],["value","branchCoverageIncreaseOnly"],["value","branchCoverageDecreaseOnly"],["value","methodCoverageIncreaseOnly"],["value","methodCoverageDecreaseOnly"],["value","fullMethodCoverageIncreaseOnly"],["value","fullMethodCoverageDecreaseOnly"],[3,"valueChange","highValueChange","value","highValue","options"],["target","_blank",3,"href"],[1,"icon-info-circled"],["codeelement-row","",3,"element","collapsed","lineCoverageAvailable","branchCoverageAvailable","methodCoverageAvailable","methodFullCoverageAvailable","visibleMetrics"],["class-row","",3,"clazz","translations","lineCoverageAvailable","branchCoverageAvailable","methodCoverageAvailable","methodFullCoverageAvailable","visibleMetrics","historyComparisionDate"],["codeelement-row","",1,"namespace",3,"element","collapsed","lineCoverageAvailable","branchCoverageAvailable","methodCoverageAvailable","methodFullCoverageAvailable","visibleMetrics"],["class-row","",1,"namespace",3,"clazz","translations","lineCoverageAvailable","branchCoverageAvailable","methodCoverageAvailable","methodFullCoverageAvailable","visibleMetrics","historyComparisionDate"]],template:function(i,r){1&i&&y(0,T8,87,62,"div"),2&i&&C(r.codeElements.length>0?0:-1)},dependencies:[Xi,kg,Lg,As,Rg,Ls,vc,ks,aI,BU,d3,g$],encapsulation:2}))}return e(),n})();class A8{constructor(){this.assembly="",this.numberOfRiskHotspots=10,this.filter="",this.sortBy="",this.sortOrder="asc"}}const Pc=(e,n,t)=>({"icon-up-dir_active":e,"icon-down-dir_active":n,"icon-up-down-dir":t}),N8=(e,n)=>({lightred:e,lightgreen:n});function O8(e,n){if(1&e&&(v(0,"option",3),D(1),_()),2&e){const t=n.$implicit;A("value",t),f(),k(t)}}function x8(e,n){if(1&e&&(v(0,"span"),D(1),_()),2&e){const t=m(2);f(),k(t.translations.top)}}function R8(e,n){1&e&&(v(0,"option",16),D(1,"20"),_())}function k8(e,n){1&e&&(v(0,"option",17),D(1,"50"),_())}function F8(e,n){1&e&&(v(0,"option",18),D(1,"100"),_())}function L8(e,n){if(1&e&&(v(0,"option",3),D(1),_()),2&e){const t=m(3);A("value",t.totalNumberOfRiskHotspots),f(),k(t.translations.all)}}function P8(e,n){if(1&e){const t=ue();v(0,"select",14),Ge("ngModelChange",function(i){B(t);const r=m(2);return ye(r.settings.numberOfRiskHotspots,i)||(r.settings.numberOfRiskHotspots=i),j(i)}),v(1,"option",15),D(2,"10"),_(),y(3,R8,2,0,"option",16),y(4,k8,2,0,"option",17),y(5,F8,2,0,"option",18),y(6,L8,2,2,"option",3),_()}if(2&e){const t=m(2);je("ngModel",t.settings.numberOfRiskHotspots),f(3),C(t.totalNumberOfRiskHotspots>10?3:-1),f(),C(t.totalNumberOfRiskHotspots>20?4:-1),f(),C(t.totalNumberOfRiskHotspots>50?5:-1),f(),C(t.totalNumberOfRiskHotspots>100?6:-1)}}function V8(e,n){1&e&&O(0,"col",11)}function H8(e,n){if(1&e){const t=ue();v(0,"th")(1,"a",12),U("click",function(i){const r=B(t).$index;return j(m(2).updateSorting(""+r,i))}),O(2,"i",13),D(3),_(),v(4,"a",19),O(5,"i",20),_()()}if(2&e){const t=n.$implicit,o=n.$index,i=m(2);f(2),A("ngClass",Ne(4,Pc,i.settings.sortBy===""+o&&"asc"===i.settings.sortOrder,i.settings.sortBy===""+o&&"desc"===i.settings.sortOrder,i.settings.sortBy!==""+o)),f(),k(t.name),f(),A("href",On(t.explanationUrl),Wn)}}function B8(e,n){if(1&e&&(v(0,"td",23),D(1),_()),2&e){const t=n.$implicit;A("ngClass",Eh(2,N8,t.exceeded,!t.exceeded)),f(),k(t.value)}}function j8(e,n){if(1&e&&(v(0,"tr")(1,"td"),D(2),_(),v(3,"td")(4,"a",21),D(5),_()(),v(6,"td",22)(7,"a",21),D(8),_()(),Qe(9,B8,2,5,"td",23,Ye),_()),2&e){const t=n.$implicit,o=m(2);f(2),k(t.assembly),f(2),A("href",t.reportPath+o.queryString,Wn),f(),k(t.class),f(),A("title",t.methodName),f(),A("href",t.reportPath+o.queryString+"#file"+t.fileIndex+"_line"+t.line,Wn),f(),P(" ",t.methodShortName," "),f(),Ke(t.metrics)}}function U8(e,n){if(1&e){const t=ue();v(0,"div")(1,"div",0)(2,"div")(3,"select",1),Ge("ngModelChange",function(i){B(t);const r=m();return ye(r.settings.assembly,i)||(r.settings.assembly=i),j(i)}),U("ngModelChange",function(){return B(t),j(m().updateRiskHotpots())}),v(4,"option",2),D(5),_(),Qe(6,O8,2,2,"option",3,Ye),_()(),v(8,"div",4),y(9,x8,2,1,"span"),y(10,P8,7,5,"select",5),_(),O(11,"div",4),v(12,"div",6)(13,"span"),D(14),_(),v(15,"input",7),Ge("ngModelChange",function(i){B(t);const r=m();return ye(r.settings.filter,i)||(r.settings.filter=i),j(i)}),U("ngModelChange",function(){return B(t),j(m().updateRiskHotpots())}),_()()(),v(16,"div",8)(17,"table",9)(18,"colgroup"),O(19,"col",10)(20,"col",10)(21,"col",10),Qe(22,V8,1,0,"col",11,Ye),_(),v(24,"thead")(25,"tr")(26,"th")(27,"a",12),U("click",function(i){return B(t),j(m().updateSorting("assembly",i))}),O(28,"i",13),D(29),_()(),v(30,"th")(31,"a",12),U("click",function(i){return B(t),j(m().updateSorting("class",i))}),O(32,"i",13),D(33),_()(),v(34,"th")(35,"a",12),U("click",function(i){return B(t),j(m().updateSorting("method",i))}),O(36,"i",13),D(37),_()(),Qe(38,H8,6,8,"th",null,Ye),_()(),v(40,"tbody"),Qe(41,j8,11,6,"tr",null,Ye),function PD(e,n){const t=K();let o;const i=e+H;t.firstCreatePass?(o=function JL(e,n){if(n)for(let t=n.length-1;t>=0;t--){const o=n[t];if(e===o.name)return o}}(n,t.pipeRegistry),t.data[i]=o,o.onDestroy&&(t.destroyHooks??=[]).push(i,o.onDestroy)):o=t.data[i];const r=o.factory||(o.factory=po(o.type)),a=pt(x);try{const l=_a(!1),c=r();return _a(l),function Iu(e,n,t,o){t>=e.data.length&&(e.data[t]=null,e.blueprint[t]=null),n[t]=o}(t,w(),i,c),c}finally{pt(a)}}(43,"slice"),_()()()()}if(2&e){const t=m();f(3),je("ngModel",t.settings.assembly),f(2),k(t.translations.assembly),f(),Ke(t.assemblies),f(3),C(t.totalNumberOfRiskHotspots>10?9:-1),f(),C(t.totalNumberOfRiskHotspots>10?10:-1),f(4),P("",t.translations.filter," "),f(),je("ngModel",t.settings.filter),f(7),Ke(t.riskHotspotMetrics),f(6),A("ngClass",Ne(16,Pc,"assembly"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"assembly"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"assembly"!==t.settings.sortBy)),f(),k(t.translations.assembly),f(3),A("ngClass",Ne(20,Pc,"class"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"class"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"class"!==t.settings.sortBy)),f(),k(t.translations.class),f(3),A("ngClass",Ne(24,Pc,"method"===t.settings.sortBy&&"asc"===t.settings.sortOrder,"method"===t.settings.sortBy&&"desc"===t.settings.sortOrder,"method"!==t.settings.sortBy)),f(),k(t.translations.method),f(),Ke(t.riskHotspotMetrics),f(3),Ke(function VD(e,n,t,o,i){const r=e+H,s=w(),a=function bo(e,n){return e[n]}(s,r);return function bs(e,n){return e[1].data[n].pure}(s,r)?kD(s,lt(),n,a.transform,t,o,i,a):a.transform(t,o,i)}(43,12,t.riskHotspots,0,t.settings.numberOfRiskHotspots))}}let $8=(()=>{var e;class n{constructor(o){this.queryString="",this.riskHotspotMetrics=[],this.riskHotspots=[],this.totalNumberOfRiskHotspots=0,this.assemblies=[],this.translations={},this.settings=new A8,this.window=o.nativeWindow}ngOnInit(){this.riskHotspotMetrics=this.window.riskHotspotMetrics,this.translations=this.window.translations,void 0!==this.window.history&&void 0!==this.window.history.replaceState&&null!==this.window.history.state&&null!=this.window.history.state.riskHotspotsSettings&&(console.log("Risk hotspots: Restoring from history",this.window.history.state.riskHotspotsSettings),this.settings=JSON.parse(JSON.stringify(this.window.history.state.riskHotspotsSettings)));const o=window.location.href.indexOf("?");o>-1&&(this.queryString=window.location.href.substring(o)),this.updateRiskHotpots()}onDonBeforeUnlodad(){if(void 0!==this.window.history&&void 0!==this.window.history.replaceState){console.log("Risk hotspots: Updating history",this.settings);let o=new lI;null!==window.history.state&&(o=JSON.parse(JSON.stringify(this.window.history.state))),o.riskHotspotsSettings=JSON.parse(JSON.stringify(this.settings)),window.history.replaceState(o,"")}}updateRiskHotpots(){const o=this.window.riskHotspots;if(this.totalNumberOfRiskHotspots=o.length,0===this.assemblies.length){let a=[];for(let l=0;l(this.\u0275fac=function(i){return new(i||n)(x(jg))},this.\u0275cmp=qt({type:n,selectors:[["risk-hotspots"]],hostBindings:function(i,r){1&i&&U("beforeunload",function(){return r.onDonBeforeUnlodad()},Ba)},standalone:!1,decls:1,vars:1,consts:[[1,"customizebox"],["name","assembly",3,"ngModelChange","ngModel"],["value",""],[3,"value"],[1,"col-center"],[3,"ngModel"],[1,"col-right"],["type","search",3,"ngModelChange","ngModel"],[1,"table-responsive"],[1,"overview","table-fixed","stripped"],[1,"column-min-200"],[1,"column105"],["href","#",3,"click"],[3,"ngClass"],[3,"ngModelChange","ngModel"],["value","10"],["value","20"],["value","50"],["value","100"],["target","_blank",3,"href"],[1,"icon-info-circled"],[3,"href"],[3,"title"],[1,"right",3,"ngClass"]],template:function(i,r){1&i&&y(0,U8,44,28,"div"),2&i&&C(r.totalNumberOfRiskHotspots>0?0:-1)},dependencies:[Xi,kg,Lg,As,Ls,vc,ks,fE],encapsulation:2}))}return e(),n})(),z8=(()=>{var e;class n{static#e=e=()=>(this.\u0275fac=function(i){return new(i||n)},this.\u0275mod=Qn({type:n,bootstrap:[$8,S8]}),this.\u0275inj=dn({providers:[jg],imports:[BH,Hj,SU]}))}return e(),n})();HH().bootstrapModule(z8).catch(e=>console.error(e))}},Yo=>{Yo(Yo.s=653)}]); \ No newline at end of file diff --git a/jobs/Backend/coveragereport/report.css b/jobs/Backend/coveragereport/report.css new file mode 100644 index 0000000000..fed1a219a3 --- /dev/null +++ b/jobs/Backend/coveragereport/report.css @@ -0,0 +1,838 @@ +:root { + color-scheme: light; + --green: #0aad0a; + --lightgreen: #dcf4dc; +} + +html { font-family: sans-serif; margin: 0; padding: 0; font-size: 0.9em; background-color: #d6d6d6; height: 100%; } +body { margin: 0; padding: 0; height: 100%; color: #000; } +h1 { font-family: 'Century Gothic', sans-serif; font-size: 1.2em; font-weight: normal; color: #fff; background-color: #6f6f6f; padding: 10px; margin: 20px -20px 20px -20px; } +h1:first-of-type { margin-top: 0; } +h2 { font-size: 1.0em; font-weight: bold; margin: 10px 0 15px 0; padding: 0; } +h3 { font-size: 1.0em; font-weight: bold; margin: 0 0 10px 0; padding: 0; display: inline-block; } +input, select, button { border: 1px solid #767676; border-radius: 0; } +button { background-color: #ddd; cursor: pointer; } +a { color: #c00; text-decoration: none; } +a:hover { color: #000; text-decoration: none; } +h1 a.back { color: #fff; background-color: #949494; display: inline-block; margin: -12px 5px -10px -10px; padding: 10px; border-right: 1px solid #fff; } +h1 a.back:hover { background-color: #ccc; } +h1 a.button { color: #000; background-color: #bebebe; margin: -5px 0 0 10px; padding: 5px 8px 5px 8px; border: 1px solid #fff; font-size: 0.9em; border-radius: 3px; float:right; } +h1 a.button:hover { background-color: #ccc; } +h1 a.button i { position: relative; top: 1px; } + +.container { margin: auto; max-width: 1650px; width: 90%; background-color: #fff; display: flex; box-shadow: 0 0 60px #7d7d7d; min-height: 100%; } +.containerleft { padding: 0 20px 20px 20px; flex: 1; min-width: 1%; } +.containerright { width: 340px; min-width: 340px; background-color: #e5e5e5; height: 100%; } +.containerrightfixed { position: fixed; padding: 0 20px 20px 20px; border-left: 1px solid #6f6f6f; width: 300px; overflow-y: auto; height: 100%; top: 0; bottom: 0; } +.containerrightfixed h1 { background-color: #c00; } +.containerrightfixed label, .containerright a { white-space: nowrap; overflow: hidden; display: inline-block; width: 100%; max-width: 300px; text-overflow: ellipsis; } +.containerright a { margin-bottom: 3px; } + +@media screen and (max-width:1200px){ + .container { box-shadow: none; width: 100%; } + .containerright { display: none; } +} + +.popup-container { position: fixed; left: 0; right: 0; top: 0; bottom: 0; background-color: rgb(0, 0, 0, 0.6); z-index: 100; } +.popup { position: absolute; top: 50%; right: 50%; transform: translate(50%,-50%); background-color: #fff; padding: 25px; border-radius: 15px; min-width: 300px; } +.popup .close { text-align: right; color: #979797; font-size: 25px;position: relative; left: 10px; bottom: 10px; cursor: pointer; } + +.footer { font-size: 0.7em; text-align: center; margin-top: 35px; } + +.card-group { display: flex; flex-wrap: wrap; margin-top: -15px; margin-left: -15px; } +.card-group + .card-group { margin-top: 0; } +.card-group .card { margin-top: 15px; margin-left: 15px; display: flex; flex-direction: column; background-color: #e4e4e4; background: radial-gradient(circle, #fefefe 0%, #f6f6f6 100%); border: 1px solid #c1c1c1; padding: 15px; color: #6f6f6f; max-width: 100% } +.card-group .card .card-header { font-size: 1.5rem; font-family: 'Century Gothic', sans-serif; margin-bottom: 15px; flex-grow: 1; } +.card-group .card .card-body { display: flex; flex-direction: row; gap: 15px; flex-grow: 1; } +.card-group .card .card-body div.table { display: flex; flex-direction: column; } +.card-group .card .large { font-size: 5rem; line-height: 5rem; font-weight: bold; align-self: flex-end; border-left-width: 4px; padding-left: 10px; } +.card-group .card table { align-self: flex-end; border-collapse: collapse; } +.card-group .card table tr { border-bottom: 1px solid #c1c1c1; } +.card-group .card table tr:hover { background-color: #c1c1c1; } +.card-group .card table tr:last-child { border-bottom: none; } +.card-group .card table th, .card-group .card table td { padding: 2px; } +.card-group td.limit-width { max-width: 200px; text-overflow: ellipsis; overflow: hidden; } +.card-group td.overflow-wrap { overflow-wrap: anywhere; } + +.pro-button { color: #fff; background-color: #20A0D2; background-image: linear-gradient(50deg, #1c7ed6 0%, #23b8cf 100%); padding: 10px; border-radius: 3px; font-weight: bold; display: inline-block; } +.pro-button:hover { color: #fff; background-color: #1C8EB7; background-image: linear-gradient(50deg, #1A6FBA 0%, #1EA1B5 100%); } +.pro-button-tiny { border-radius: 10px; padding: 3px 8px; } + +th { text-align: left; } +.table-fixed { table-layout: fixed; } +.table-responsive { overflow-x: auto; } +.table-responsive::-webkit-scrollbar { height: 20px; } +.table-responsive::-webkit-scrollbar-thumb { background-color: #6f6f6f; border-radius: 20px; border: 5px solid #fff; } +.overview { border: 1px solid #c1c1c1; border-collapse: collapse; width: 100%; word-wrap: break-word; } +.overview th { border: 1px solid #c1c1c1; border-collapse: collapse; padding: 2px 4px 2px 4px; background-color: #ddd; } +.overview tr.namespace th { background-color: #dcdcdc; } +.overview thead th { background-color: #d1d1d1; } +.overview th a { color: #000; } +.overview tr.namespace a { margin-left: 15px; display: block; } +.overview td { border: 1px solid #c1c1c1; border-collapse: collapse; padding: 2px 5px 2px 5px; } +.overview tr.filterbar td { height: 60px; } +.overview tr.header th { background-color: #d1d1d1; } +.overview tr.header th:nth-child(2n+1) { background-color: #ddd; } +.overview tr.header th:first-child { border-left: 1px solid #fff; border-top: 1px solid #fff; background-color: #fff; } +.overview tbody tr:hover>td { background-color: #b0b0b0; } + +div.currenthistory { margin: -2px -5px 0 -5px; padding: 2px 5px 2px 5px; height: 16px; } +.coverage { border-collapse: collapse; font-size: 5px; height: 10px; } +.coverage td { padding: 0; border: none; } +.stripped tr:nth-child(2n+1) { background-color: #F3F3F3; } + +.customizebox { font-size: 0.75em; margin-bottom: 7px; display: grid; grid-template-columns: 1fr; grid-template-rows: auto auto auto auto; grid-column-gap: 10px; grid-row-gap: 10px; } +.customizebox>div { align-self: end; } +.customizebox div.col-right input { width: 150px; } + +@media screen and (min-width: 1000px) { + .customizebox { grid-template-columns: repeat(4, 1fr); grid-template-rows: 1fr; } + .customizebox div.col-center { justify-self: center; } + .customizebox div.col-right { justify-self: end; } +} +.slider-label { position: relative; left: 85px; } + +.percentagebar { + padding-left: 3px; +} +a.percentagebar { + padding-left: 6px; +} +.percentagebarundefined { + border-left: 2px solid #fff; +} +.percentagebar0 { + border-left: 2px solid #c10909; +} +.percentagebar10 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 90%, var(--green) 90%, var(--green) 100%) 1; +} +.percentagebar20 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 80%, var(--green) 80%, var(--green) 100%) 1; +} +.percentagebar30 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 70%, var(--green) 70%, var(--green) 100%) 1; +} +.percentagebar40 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 60%, var(--green) 60%, var(--green) 100%) 1; +} +.percentagebar50 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 50%, var(--green) 50%, var(--green) 100%) 1; +} +.percentagebar60 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 40%, var(--green) 40%, var(--green) 100%) 1; +} +.percentagebar70 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 30%, var(--green) 30%, var(--green) 100%) 1; +} +.percentagebar80 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 20%, var(--green) 20%, var(--green) 100%) 1; +} +.percentagebar90 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 10%, var(--green) 10%, var(--green) 100%) 1; +} +.percentagebar100 { + border-left: 2px solid var(--green); +} + +.mt-1 { margin-top: 4px; } +.hidden, .ng-hide { display: none; } +.right { text-align: right; } +.center { text-align: center; } +.rightmargin { padding-right: 8px; } +.leftmargin { padding-left: 5px; } +.green { background-color: var(--green); } +.lightgreen { background-color: var(--lightgreen); } +.red { background-color: #c10909; } +.lightred { background-color: #f7dede; } +.orange { background-color: #FFA500; } +.lightorange { background-color: #FFEFD5; } +.gray { background-color: #dcdcdc; } +.lightgray { color: #888888; } +.lightgraybg { background-color: #dadada; } + +code { font-family: Consolas, monospace; font-size: 0.9em; } + +.toggleZoom { text-align:right; } + +.historychart svg { max-width: 100%; } +.ct-chart { position: relative; } +.ct-chart .ct-line { stroke-width: 2px !important; } +.ct-chart .ct-point { stroke-width: 6px !important; transition: stroke-width .2s; } +.ct-chart .ct-point:hover { stroke-width: 10px !important; } +.ct-chart .ct-series.ct-series-a .ct-line, .ct-chart .ct-series.ct-series-a .ct-point { stroke: #c00 !important;} +.ct-chart .ct-series.ct-series-b .ct-line, .ct-chart .ct-series.ct-series-b .ct-point { stroke: #1c2298 !important;} +.ct-chart .ct-series.ct-series-c .ct-line, .ct-chart .ct-series.ct-series-c .ct-point { stroke: #0aad0a !important;} +.ct-chart .ct-series.ct-series-d .ct-line, .ct-chart .ct-series.ct-series-d .ct-point { stroke: #FF6A00 !important;} + +.tinylinecoveragechart, .tinybranchcoveragechart, .tinymethodcoveragechart, .tinyfullmethodcoveragechart { background-color: #fff; margin-left: -3px; float: left; border: 1px solid #c1c1c1; width: 30px; height: 18px; } +.historiccoverageoffset { margin-top: 7px; } + +.tinylinecoveragechart .ct-line, .tinybranchcoveragechart .ct-line, .tinymethodcoveragechart .ct-line, .tinyfullmethodcoveragechart .ct-line { stroke-width: 1px !important; } +.tinybranchcoveragechart .ct-series.ct-series-a .ct-line { stroke: #1c2298 !important; } +.tinymethodcoveragechart .ct-series.ct-series-a .ct-line { stroke: #0aad0a !important; } +.tinyfullmethodcoveragechart .ct-series.ct-series-a .ct-line { stroke: #FF6A00 !important; } + +.linecoverage { background-color: #c00; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } +.branchcoverage { background-color: #1c2298; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } +.codeelementcoverage { background-color: #0aad0a; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } +.fullcodeelementcoverage { background-color: #FF6A00; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } + +.tooltip { position: absolute; display: none; padding: 5px; background: #F4C63D; color: #453D3F; pointer-events: none; z-index: 1; min-width: 250px; } + +.column-min-200 { min-width: 200px; } +.column70 { width: 70px; } +.column90 { width: 90px; } +.column98 { width: 98px; } +.column100 { width: 100px; } +.column105 { width: 105px; } +.column112 { width: 112px; } + +.cardpercentagebar { border-left-style: solid; } +.cardpercentagebar0 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 0%, var(--green) 0%) 1; } +.cardpercentagebar1 { border-image: linear-gradient(to bottom, #c10909 1%, #c10909 1%, var(--green) 1%) 1; } +.cardpercentagebar2 { border-image: linear-gradient(to bottom, #c10909 2%, #c10909 2%, var(--green) 2%) 1; } +.cardpercentagebar3 { border-image: linear-gradient(to bottom, #c10909 3%, #c10909 3%, var(--green) 3%) 1; } +.cardpercentagebar4 { border-image: linear-gradient(to bottom, #c10909 4%, #c10909 4%, var(--green) 4%) 1; } +.cardpercentagebar5 { border-image: linear-gradient(to bottom, #c10909 5%, #c10909 5%, var(--green) 5%) 1; } +.cardpercentagebar6 { border-image: linear-gradient(to bottom, #c10909 6%, #c10909 6%, var(--green) 6%) 1; } +.cardpercentagebar7 { border-image: linear-gradient(to bottom, #c10909 7%, #c10909 7%, var(--green) 7%) 1; } +.cardpercentagebar8 { border-image: linear-gradient(to bottom, #c10909 8%, #c10909 8%, var(--green) 8%) 1; } +.cardpercentagebar9 { border-image: linear-gradient(to bottom, #c10909 9%, #c10909 9%, var(--green) 9%) 1; } +.cardpercentagebar10 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 10%, var(--green) 10%) 1; } +.cardpercentagebar11 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 11%, var(--green) 11%) 1; } +.cardpercentagebar12 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 12%, var(--green) 12%) 1; } +.cardpercentagebar13 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 13%, var(--green) 13%) 1; } +.cardpercentagebar14 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 14%, var(--green) 14%) 1; } +.cardpercentagebar15 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 15%, var(--green) 15%) 1; } +.cardpercentagebar16 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 16%, var(--green) 16%) 1; } +.cardpercentagebar17 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 17%, var(--green) 17%) 1; } +.cardpercentagebar18 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 18%, var(--green) 18%) 1; } +.cardpercentagebar19 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 19%, var(--green) 19%) 1; } +.cardpercentagebar20 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 20%, var(--green) 20%) 1; } +.cardpercentagebar21 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 21%, var(--green) 21%) 1; } +.cardpercentagebar22 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 22%, var(--green) 22%) 1; } +.cardpercentagebar23 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 23%, var(--green) 23%) 1; } +.cardpercentagebar24 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 24%, var(--green) 24%) 1; } +.cardpercentagebar25 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 25%, var(--green) 25%) 1; } +.cardpercentagebar26 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 26%, var(--green) 26%) 1; } +.cardpercentagebar27 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 27%, var(--green) 27%) 1; } +.cardpercentagebar28 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 28%, var(--green) 28%) 1; } +.cardpercentagebar29 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 29%, var(--green) 29%) 1; } +.cardpercentagebar30 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 30%, var(--green) 30%) 1; } +.cardpercentagebar31 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 31%, var(--green) 31%) 1; } +.cardpercentagebar32 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 32%, var(--green) 32%) 1; } +.cardpercentagebar33 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 33%, var(--green) 33%) 1; } +.cardpercentagebar34 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 34%, var(--green) 34%) 1; } +.cardpercentagebar35 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 35%, var(--green) 35%) 1; } +.cardpercentagebar36 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 36%, var(--green) 36%) 1; } +.cardpercentagebar37 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 37%, var(--green) 37%) 1; } +.cardpercentagebar38 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 38%, var(--green) 38%) 1; } +.cardpercentagebar39 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 39%, var(--green) 39%) 1; } +.cardpercentagebar40 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 40%, var(--green) 40%) 1; } +.cardpercentagebar41 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 41%, var(--green) 41%) 1; } +.cardpercentagebar42 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 42%, var(--green) 42%) 1; } +.cardpercentagebar43 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 43%, var(--green) 43%) 1; } +.cardpercentagebar44 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 44%, var(--green) 44%) 1; } +.cardpercentagebar45 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 45%, var(--green) 45%) 1; } +.cardpercentagebar46 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 46%, var(--green) 46%) 1; } +.cardpercentagebar47 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 47%, var(--green) 47%) 1; } +.cardpercentagebar48 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 48%, var(--green) 48%) 1; } +.cardpercentagebar49 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 49%, var(--green) 49%) 1; } +.cardpercentagebar50 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 50%, var(--green) 50%) 1; } +.cardpercentagebar51 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 51%, var(--green) 51%) 1; } +.cardpercentagebar52 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 52%, var(--green) 52%) 1; } +.cardpercentagebar53 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 53%, var(--green) 53%) 1; } +.cardpercentagebar54 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 54%, var(--green) 54%) 1; } +.cardpercentagebar55 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 55%, var(--green) 55%) 1; } +.cardpercentagebar56 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 56%, var(--green) 56%) 1; } +.cardpercentagebar57 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 57%, var(--green) 57%) 1; } +.cardpercentagebar58 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 58%, var(--green) 58%) 1; } +.cardpercentagebar59 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 59%, var(--green) 59%) 1; } +.cardpercentagebar60 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 60%, var(--green) 60%) 1; } +.cardpercentagebar61 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 61%, var(--green) 61%) 1; } +.cardpercentagebar62 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 62%, var(--green) 62%) 1; } +.cardpercentagebar63 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 63%, var(--green) 63%) 1; } +.cardpercentagebar64 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 64%, var(--green) 64%) 1; } +.cardpercentagebar65 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 65%, var(--green) 65%) 1; } +.cardpercentagebar66 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 66%, var(--green) 66%) 1; } +.cardpercentagebar67 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 67%, var(--green) 67%) 1; } +.cardpercentagebar68 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 68%, var(--green) 68%) 1; } +.cardpercentagebar69 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 69%, var(--green) 69%) 1; } +.cardpercentagebar70 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 70%, var(--green) 70%) 1; } +.cardpercentagebar71 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 71%, var(--green) 71%) 1; } +.cardpercentagebar72 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 72%, var(--green) 72%) 1; } +.cardpercentagebar73 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 73%, var(--green) 73%) 1; } +.cardpercentagebar74 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 74%, var(--green) 74%) 1; } +.cardpercentagebar75 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 75%, var(--green) 75%) 1; } +.cardpercentagebar76 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 76%, var(--green) 76%) 1; } +.cardpercentagebar77 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 77%, var(--green) 77%) 1; } +.cardpercentagebar78 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 78%, var(--green) 78%) 1; } +.cardpercentagebar79 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 79%, var(--green) 79%) 1; } +.cardpercentagebar80 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 80%, var(--green) 80%) 1; } +.cardpercentagebar81 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 81%, var(--green) 81%) 1; } +.cardpercentagebar82 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 82%, var(--green) 82%) 1; } +.cardpercentagebar83 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 83%, var(--green) 83%) 1; } +.cardpercentagebar84 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 84%, var(--green) 84%) 1; } +.cardpercentagebar85 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 85%, var(--green) 85%) 1; } +.cardpercentagebar86 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 86%, var(--green) 86%) 1; } +.cardpercentagebar87 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 87%, var(--green) 87%) 1; } +.cardpercentagebar88 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 88%, var(--green) 88%) 1; } +.cardpercentagebar89 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 89%, var(--green) 89%) 1; } +.cardpercentagebar90 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 90%, var(--green) 90%) 1; } +.cardpercentagebar91 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 91%, var(--green) 91%) 1; } +.cardpercentagebar92 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 92%, var(--green) 92%) 1; } +.cardpercentagebar93 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 93%, var(--green) 93%) 1; } +.cardpercentagebar94 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 94%, var(--green) 94%) 1; } +.cardpercentagebar95 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 95%, var(--green) 95%) 1; } +.cardpercentagebar96 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 96%, var(--green) 96%) 1; } +.cardpercentagebar97 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 97%, var(--green) 97%) 1; } +.cardpercentagebar98 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 98%, var(--green) 98%) 1; } +.cardpercentagebar99 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 99%, var(--green) 99%) 1; } +.cardpercentagebar100 { border-image: linear-gradient(to bottom, #c10909 0%, #c10909 100%, var(--green) 100%) 1; } + +.covered0 { width: 0px; } +.covered1 { width: 1px; } +.covered2 { width: 2px; } +.covered3 { width: 3px; } +.covered4 { width: 4px; } +.covered5 { width: 5px; } +.covered6 { width: 6px; } +.covered7 { width: 7px; } +.covered8 { width: 8px; } +.covered9 { width: 9px; } +.covered10 { width: 10px; } +.covered11 { width: 11px; } +.covered12 { width: 12px; } +.covered13 { width: 13px; } +.covered14 { width: 14px; } +.covered15 { width: 15px; } +.covered16 { width: 16px; } +.covered17 { width: 17px; } +.covered18 { width: 18px; } +.covered19 { width: 19px; } +.covered20 { width: 20px; } +.covered21 { width: 21px; } +.covered22 { width: 22px; } +.covered23 { width: 23px; } +.covered24 { width: 24px; } +.covered25 { width: 25px; } +.covered26 { width: 26px; } +.covered27 { width: 27px; } +.covered28 { width: 28px; } +.covered29 { width: 29px; } +.covered30 { width: 30px; } +.covered31 { width: 31px; } +.covered32 { width: 32px; } +.covered33 { width: 33px; } +.covered34 { width: 34px; } +.covered35 { width: 35px; } +.covered36 { width: 36px; } +.covered37 { width: 37px; } +.covered38 { width: 38px; } +.covered39 { width: 39px; } +.covered40 { width: 40px; } +.covered41 { width: 41px; } +.covered42 { width: 42px; } +.covered43 { width: 43px; } +.covered44 { width: 44px; } +.covered45 { width: 45px; } +.covered46 { width: 46px; } +.covered47 { width: 47px; } +.covered48 { width: 48px; } +.covered49 { width: 49px; } +.covered50 { width: 50px; } +.covered51 { width: 51px; } +.covered52 { width: 52px; } +.covered53 { width: 53px; } +.covered54 { width: 54px; } +.covered55 { width: 55px; } +.covered56 { width: 56px; } +.covered57 { width: 57px; } +.covered58 { width: 58px; } +.covered59 { width: 59px; } +.covered60 { width: 60px; } +.covered61 { width: 61px; } +.covered62 { width: 62px; } +.covered63 { width: 63px; } +.covered64 { width: 64px; } +.covered65 { width: 65px; } +.covered66 { width: 66px; } +.covered67 { width: 67px; } +.covered68 { width: 68px; } +.covered69 { width: 69px; } +.covered70 { width: 70px; } +.covered71 { width: 71px; } +.covered72 { width: 72px; } +.covered73 { width: 73px; } +.covered74 { width: 74px; } +.covered75 { width: 75px; } +.covered76 { width: 76px; } +.covered77 { width: 77px; } +.covered78 { width: 78px; } +.covered79 { width: 79px; } +.covered80 { width: 80px; } +.covered81 { width: 81px; } +.covered82 { width: 82px; } +.covered83 { width: 83px; } +.covered84 { width: 84px; } +.covered85 { width: 85px; } +.covered86 { width: 86px; } +.covered87 { width: 87px; } +.covered88 { width: 88px; } +.covered89 { width: 89px; } +.covered90 { width: 90px; } +.covered91 { width: 91px; } +.covered92 { width: 92px; } +.covered93 { width: 93px; } +.covered94 { width: 94px; } +.covered95 { width: 95px; } +.covered96 { width: 96px; } +.covered97 { width: 97px; } +.covered98 { width: 98px; } +.covered99 { width: 99px; } +.covered100 { width: 100px; } + + @media print { + html, body { background-color: #fff; } + .container { max-width: 100%; width: 100%; padding: 0; } + .overview colgroup col:first-child { width: 300px; } +} + +.icon-up-down-dir { + background-image: url(icon_up-down-dir.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Im0gMTQwOCw3NDIgcSAwLDI2IC0xOSw0NSAtMTksMTkgLTQ1LDE5IEggNDQ4IHEgLTI2LDAgLTQ1LC0xOSAtMTksLTE5IC0xOSwtNDUgMCwtMjYgMTksLTQ1IEwgODUxLDI0OSBxIDE5LC0xOSA0NSwtMTkgMjYsMCA0NSwxOSBsIDQ0OCw0NDggcSAxOSwxOSAxOSw0NSB6IiAvPjxwYXRoIGQ9Im0gMTQwOCwxMDUwIHEgMCwyNiAtMTksNDUgbCAtNDQ4LDQ0OCBxIC0xOSwxOSAtNDUsMTkgLTI2LDAgLTQ1LC0xOSBMIDQwMywxMDk1IHEgLTE5LC0xOSAtMTksLTQ1IDAsLTI2IDE5LC00NSAxOSwtMTkgNDUsLTE5IGggODk2IHEgMjYsMCA0NSwxOSAxOSwxOSAxOSw0NSB6IiAvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-up-dir_active { + background-image: url(icon_up-dir.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDEyMTZxMCAyNi0xOSA0NXQtNDUgMTloLTg5NnEtMjYgMC00NS0xOXQtMTktNDUgMTktNDVsNDQ4LTQ0OHExOS0xOSA0NS0xOXQ0NSAxOWw0NDggNDQ4cTE5IDE5IDE5IDQ1eiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-down-dir_active { + background-image: url(icon_up-dir_active.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDcwNHEwIDI2LTE5IDQ1bC00NDggNDQ4cS0xOSAxOS00NSAxOXQtNDUtMTlsLTQ0OC00NDhxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1eiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-info-circled { + background-image: url(icon_info-circled.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxjaXJjbGUgY3g9Ijg5NiIgY3k9Ijg5NiIgcj0iNzUwIiBmaWxsPSIjZmZmIiAvPjxwYXRoIGZpbGw9IiMyOEE1RkYiIGQ9Ik0xMTUyIDEzNzZ2LTE2MHEwLTE0LTktMjN0LTIzLTloLTk2di01MTJxMC0xNC05LTIzdC0yMy05aC0zMjBxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloOTZ2MzIwaC05NnEtMTQgMC0yMyA5dC05IDIzdjE2MHEwIDE0IDkgMjN0MjMgOWg0NDhxMTQgMCAyMy05dDktMjN6bS0xMjgtODk2di0xNjBxMC0xNC05LTIzdC0yMy05aC0xOTJxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMtOXQ5LTIzem02NDAgNDE2cTAgMjA5LTEwMyAzODUuNXQtMjc5LjUgMjc5LjUtMzg1LjUgMTAzLTM4NS41LTEwMy0yNzkuNS0yNzkuNS0xMDMtMzg1LjUgMTAzLTM4NS41IDI3OS41LTI3OS41IDM4NS41LTEwMyAzODUuNSAxMDMgMjc5LjUgMjc5LjUgMTAzIDM4NS41eiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; +} +.icon-plus { + background-image: url(icon_plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTQxNnY0MTZxMCA0MC0yOCA2OHQtNjggMjhoLTE5MnEtNDAgMC02OC0yOHQtMjgtNjh2LTQxNmgtNDE2cS00MCAwLTY4LTI4dC0yOC02OHYtMTkycTAtNDAgMjgtNjh0NjgtMjhoNDE2di00MTZxMC00MCAyOC02OHQ2OC0yOGgxOTJxNDAgMCA2OCAyOHQyOCA2OHY0MTZoNDE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-minus { + background-image: url(icon_minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTEyMTZxLTQwIDAtNjgtMjh0LTI4LTY4di0xOTJxMC00MCAyOC02OHQ2OC0yOGgxMjE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-wrench { + background-image: url(icon_wrench.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik00NDggMTQ3MnEwLTI2LTE5LTQ1dC00NS0xOS00NSAxOS0xOSA0NSAxOSA0NSA0NSAxOSA0NS0xOSAxOS00NXptNjQ0LTQyMGwtNjgyIDY4MnEtMzcgMzctOTAgMzctNTIgMC05MS0zN2wtMTA2LTEwOHEtMzgtMzYtMzgtOTAgMC01MyAzOC05MWw2ODEtNjgxcTM5IDk4IDExNC41IDE3My41dDE3My41IDExNC41em02MzQtNDM1cTAgMzktMjMgMTA2LTQ3IDEzNC0xNjQuNSAyMTcuNXQtMjU4LjUgODMuNXEtMTg1IDAtMzE2LjUtMTMxLjV0LTEzMS41LTMxNi41IDEzMS41LTMxNi41IDMxNi41LTEzMS41cTU4IDAgMTIxLjUgMTYuNXQxMDcuNSA0Ni41cTE2IDExIDE2IDI4dC0xNiAyOGwtMjkzIDE2OXYyMjRsMTkzIDEwN3E1LTMgNzktNDguNXQxMzUuNS04MSA3MC41LTM1LjVxMTUgMCAyMy41IDEwdDguNSAyNXoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-cog { + background-image: url(icon_cog.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNDQ0Ljc4OCAyOTEuMWw0Mi42MTYgMjQuNTk5YzQuODY3IDIuODA5IDcuMTI2IDguNjE4IDUuNDU5IDEzLjk4NS0xMS4wNyAzNS42NDItMjkuOTcgNjcuODQyLTU0LjY4OSA5NC41ODZhMTIuMDE2IDEyLjAxNiAwIDAgMS0xNC44MzIgMi4yNTRsLTQyLjU4NC0yNC41OTVhMTkxLjU3NyAxOTEuNTc3IDAgMCAxLTYwLjc1OSAzNS4xM3Y0OS4xODJhMTIuMDEgMTIuMDEgMCAwIDEtOS4zNzcgMTEuNzE4Yy0zNC45NTYgNy44NS03Mi40OTkgOC4yNTYtMTA5LjIxOS4wMDctNS40OS0xLjIzMy05LjQwMy02LjA5Ni05LjQwMy0xMS43MjN2LTQ5LjE4NGExOTEuNTU1IDE5MS41NTUgMCAwIDEtNjAuNzU5LTM1LjEzbC00Mi41ODQgMjQuNTk1YTEyLjAxNiAxMi4wMTYgMCAwIDEtMTQuODMyLTIuMjU0Yy0yNC43MTgtMjYuNzQ0LTQzLjYxOS01OC45NDQtNTQuNjg5LTk0LjU4Ni0xLjY2Ny01LjM2Ni41OTItMTEuMTc1IDUuNDU5LTEzLjk4NUw2Ny4yMTIgMjkxLjFhMTkzLjQ4IDE5My40OCAwIDAgMSAwLTcwLjE5OWwtNDIuNjE2LTI0LjU5OWMtNC44NjctMi44MDktNy4xMjYtOC42MTgtNS40NTktMTMuOTg1IDExLjA3LTM1LjY0MiAyOS45Ny02Ny44NDIgNTQuNjg5LTk0LjU4NmExMi4wMTYgMTIuMDE2IDAgMCAxIDE0LjgzMi0yLjI1NGw0Mi41ODQgMjQuNTk1YTE5MS41NzcgMTkxLjU3NyAwIDAgMSA2MC43NTktMzUuMTNWMjUuNzU5YTEyLjAxIDEyLjAxIDAgMCAxIDkuMzc3LTExLjcxOGMzNC45NTYtNy44NSA3Mi40OTktOC4yNTYgMTA5LjIxOS0uMDA3IDUuNDkgMS4yMzMgOS40MDMgNi4wOTYgOS40MDMgMTEuNzIzdjQ5LjE4NGExOTEuNTU1IDE5MS41NTUgMCAwIDEgNjAuNzU5IDM1LjEzbDQyLjU4NC0yNC41OTVhMTIuMDE2IDEyLjAxNiAwIDAgMSAxNC44MzIgMi4yNTRjMjQuNzE4IDI2Ljc0NCA0My42MTkgNTguOTQ0IDU0LjY4OSA5NC41ODYgMS42NjcgNS4zNjYtLjU5MiAxMS4xNzUtNS40NTkgMTMuOTg1TDQ0NC43ODggMjIwLjlhMTkzLjQ4NSAxOTMuNDg1IDAgMCAxIDAgNzAuMnpNMzM2IDI1NmMwLTQ0LjExMi0zNS44ODgtODAtODAtODBzLTgwIDM1Ljg4OC04MCA4MCAzNS44ODggODAgODAgODAgODAtMzUuODg4IDgwLTgweiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 16px; + height: 0.8em; + display: inline-block; +} +.icon-fork { + background-image: url(icon_fork.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNmZmYiIC8+PHBhdGggZD0iTTY3MiAxNDcycTAtNDAtMjgtNjh0LTY4LTI4LTY4IDI4LTI4IDY4IDI4IDY4IDY4IDI4IDY4LTI4IDI4LTY4em0wLTExNTJxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTY0MCAxMjhxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTk2IDBxMCA1Mi0yNiA5Ni41dC03MCA2OS41cS0yIDI4Ny0yMjYgNDE0LTY3IDM4LTIwMyA4MS0xMjggNDAtMTY5LjUgNzF0LTQxLjUgMTAwdjI2cTQ0IDI1IDcwIDY5LjV0MjYgOTYuNXEwIDgwLTU2IDEzNnQtMTM2IDU2LTEzNi01Ni01Ni0xMzZxMC01MiAyNi05Ni41dDcwLTY5LjV2LTgyMHEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnEwIDUyLTI2IDk2LjV0LTcwIDY5LjV2NDk3cTU0LTI2IDE1NC01NyA1NS0xNyA4Ny41LTI5LjV0NzAuNS0zMSA1OS0zOS41IDQwLjUtNTEgMjgtNjkuNSA4LjUtOTEuNXEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-cube { + background-image: url(icon_cube.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik04OTYgMTYyOWw2NDAtMzQ5di02MzZsLTY0MCAyMzN2NzUyem0tNjQtODY1bDY5OC0yNTQtNjk4LTI1NC02OTggMjU0em04MzItMjUydjc2OHEwIDM1LTE4IDY1dC00OSA0N2wtNzA0IDM4NHEtMjggMTYtNjEgMTZ0LTYxLTE2bC03MDQtMzg0cS0zMS0xNy00OS00N3QtMTgtNjV2LTc2OHEwLTQwIDIzLTczdDYxLTQ3bDcwNC0yNTZxMjItOCA0NC04dDQ0IDhsNzA0IDI1NnEzOCAxNCA2MSA0N3QyMyA3M3oiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-search-plus { + background-image: url(icon_search-plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtMjI0djIyNHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNjRxLTEzIDAtMjIuNS05LjV0LTkuNS0yMi41di0yMjRoLTIyNHEtMTMgMC0yMi41LTkuNXQtOS41LTIyLjV2LTY0cTAtMTMgOS41LTIyLjV0MjIuNS05LjVoMjI0di0yMjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg2NHExMyAwIDIyLjUgOS41dDkuNSAyMi41djIyNGgyMjRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-search-minus { + background-image: url(icon_search-minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNTc2cS0xMyAwLTIyLjUtOS41dC05LjUtMjIuNXYtNjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg1NzZxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-star { + background-image: url(icon_star.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNzI4IDY0N3EwIDIyLTI2IDQ4bC0zNjMgMzU0IDg2IDUwMHExIDcgMSAyMCAwIDIxLTEwLjUgMzUuNXQtMzAuNSAxNC41cS0xOSAwLTQwLTEybC00NDktMjM2LTQ0OSAyMzZxLTIyIDEyLTQwIDEyLTIxIDAtMzEuNS0xNC41dC0xMC41LTM1LjVxMC02IDItMjBsODYtNTAwLTM2NC0zNTRxLTI1LTI3LTI1LTQ4IDAtMzcgNTYtNDZsNTAyLTczIDIyNS00NTVxMTktNDEgNDktNDF0NDkgNDFsMjI1IDQ1NSA1MDIgNzNxNTYgOSA1NiA0NnoiIGZpbGw9IiMwMDAiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-sponsor { + background-image: url(icon_sponsor.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik04OTYgMTY2NHEtMjYgMC00NC0xOGwtNjI0LTYwMnEtMTAtOC0yNy41LTI2dC01NS41LTY1LjUtNjgtOTcuNS01My41LTEyMS0yMy41LTEzOHEwLTIyMCAxMjctMzQ0dDM1MS0xMjRxNjIgMCAxMjYuNSAyMS41dDEyMCA1OCA5NS41IDY4LjUgNzYgNjhxMzYtMzYgNzYtNjh0OTUuNS02OC41IDEyMC01OCAxMjYuNS0yMS41cTIyNCAwIDM1MSAxMjR0MTI3IDM0NHEwIDIyMS0yMjkgNDUwbC02MjMgNjAwcS0xOCAxOC00NCAxOHoiIGZpbGw9IiNlYTRhYWEiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} + +.ngx-slider .ngx-slider-bar { + background: #a9a9a9 !important; +} + +.ngx-slider .ngx-slider-selection { + background: #818181 !important; +} + +.ngx-slider .ngx-slider-bubble { + padding: 3px 4px !important; + font-size: 12px !important; +} + +.ngx-slider .ngx-slider-pointer { + width: 20px !important; + height: 20px !important; + top: -8px !important; + background-color: #0075FF !important; + -webkit-border-radius: 10px !important; + -moz-border-radius: 10px !important; + border-radius: 10px !important; +} + + .ngx-slider .ngx-slider-pointer:after { + content: none !important; + } + +.ngx-slider .ngx-slider-tick.ngx-slider-selected { + background-color: #62a5f4 !important; + width: 8px !important; + height: 8px !important; + top: 1px !important; +} + + + +:root { + color-scheme: light dark; +} + +@media (prefers-color-scheme: dark) { + @media screen { + html { + background-color: #333; + color: #fff; + } + + body { + color: #fff; + } + + h1 { + background-color: #555453; + color: #fff; + } + + .container { + background-color: #333; + box-shadow: 0 0 60px #0c0c0c; + } + + .containerrightfixed { + background-color: #3D3C3C; + border-left: 1px solid #515050; + } + + .containerrightfixed h1 { + background-color: #484747; + } + + .popup-container { + background-color: rgb(80, 80, 80, 0.6); + } + + .popup { + background-color: #333; + } + + .card-group .card { + background-color: #333; + background: radial-gradient(circle, #444 0%, #333 100%); + border: 1px solid #545454; + color: #fff; + } + + .card-group .card table tr { + border-bottom: 1px solid #545454; + } + + .card-group .card table tr:hover { + background-color: #2E2D2C; + } + + .table-responsive::-webkit-scrollbar-thumb { + background-color: #555453; + border: 5px solid #333; + } + + .overview tr:hover > td { + background-color: #2E2D2C; + } + + .overview th { + background-color: #444; + border: 1px solid #3B3A39; + } + + .overview tr.namespace th { + background-color: #444; + } + + .overview thead th { + background-color: #444; + } + + .overview th a { + color: #fff; + color: rgba(255, 255, 255, 0.95); + } + + .overview th a:hover { + color: #0078d4; + } + + .overview td { + border: 1px solid #3B3A39; + } + + .overview .coverage td { + border: none; + } + + .overview tr.header th { + background-color: #444; + } + + .overview tr.header th:nth-child(2n+1) { + background-color: #3a3a3a; + } + + .overview tr.header th:first-child { + border-left: 1px solid #333; + border-top: 1px solid #333; + background-color: #333; + } + + .stripped tr:nth-child(2n+1) { + background-color: #3c3c3c; + } + + input, select, button { + background-color: #333; + color: #fff; + border: 1px solid #A19F9D; + } + + a { + color: #fff; + color: rgba(255, 255, 255, 0.95); + } + + a:hover { + color: #0078d4; + } + + h1 a.back { + background-color: #4a4846; + } + + h1 a.button { + color: #fff; + background-color: #565656; + border-color: #c1c1c1; + } + + h1 a.button:hover { + background-color: #8d8d8d; + } + + .gray { + background-color: #484747; + } + + .lightgray { + color: #ebebeb; + } + + .lightgraybg { + background-color: #474747; + } + + .lightgreen { + background-color: #406540; + } + + .lightorange { + background-color: #ab7f36; + } + + .lightred { + background-color: #954848; + } + + .ct-label { + color: #fff !important; + fill: #fff !important; + } + + .ct-grid { + stroke: #fff !important; + } + + .ct-chart .ct-series.ct-series-a .ct-line, .ct-chart .ct-series.ct-series-a .ct-point { + stroke: #0078D4 !important; + } + + .ct-chart .ct-series.ct-series-b .ct-line, .ct-chart .ct-series.ct-series-b .ct-point { + stroke: #6dc428 !important; + } + + .ct-chart .ct-series.ct-series-c .ct-line, .ct-chart .ct-series.ct-series-c .ct-point { + stroke: #e58f1d !important; + } + + .ct-chart .ct-series.ct-series-d .ct-line, .ct-chart .ct-series.ct-series-d .ct-point { + stroke: #c71bca !important; + } + + .linecoverage { + background-color: #0078D4; + } + + .branchcoverage { + background-color: #6dc428; + } + .codeelementcoverage { + background-color: #e58f1d; + } + + .fullcodeelementcoverage { + background-color: #c71bca; + } + + .tinylinecoveragechart, .tinybranchcoveragechart, .tinymethodcoveragechart, .tinyfullmethodcoveragechart { + background-color: #333; + } + + .tinybranchcoveragechart .ct-series.ct-series-a .ct-line { + stroke: #6dc428 !important; + } + + .tinymethodcoveragechart .ct-series.ct-series-a .ct-line { + stroke: #e58f1d !important; + } + + .tinyfullmethodcoveragechart .ct-series.ct-series-a .ct-line { + stroke: #c71bca !important; + } + + .icon-up-down-dir { + background-image: url(icon_up-down-dir_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNCRkJGQzAiIGQ9Im0gMTQwOCw3NDIgcSAwLDI2IC0xOSw0NSAtMTksMTkgLTQ1LDE5IEggNDQ4IHEgLTI2LDAgLTQ1LC0xOSAtMTksLTE5IC0xOSwtNDUgMCwtMjYgMTksLTQ1IEwgODUxLDI0OSBxIDE5LC0xOSA0NSwtMTkgMjYsMCA0NSwxOSBsIDQ0OCw0NDggcSAxOSwxOSAxOSw0NSB6IiAvPjxwYXRoIGZpbGw9IiNCRkJGQzAiIGQ9Im0gMTQwOCwxMDUwIHEgMCwyNiAtMTksNDUgbCAtNDQ4LDQ0OCBxIC0xOSwxOSAtNDUsMTkgLTI2LDAgLTQ1LC0xOSBMIDQwMywxMDk1IHEgLTE5LC0xOSAtMTksLTQ1IDAsLTI2IDE5LC00NSAxOSwtMTkgNDUsLTE5IGggODk2IHEgMjYsMCA0NSwxOSAxOSwxOSAxOSw0NSB6IiAvPjwvc3ZnPg==); + } + .icon-info-circled { + background-image: url(icon_info-circled_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxjaXJjbGUgY3g9Ijg5NiIgY3k9Ijg5NiIgcj0iNzUwIiBmaWxsPSIjZmZmIiAvPjxwYXRoIGZpbGw9IiMyOEE1RkYiIGQ9Ik0xMTUyIDEzNzZ2LTE2MHEwLTE0LTktMjN0LTIzLTloLTk2di01MTJxMC0xNC05LTIzdC0yMy05aC0zMjBxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloOTZ2MzIwaC05NnEtMTQgMC0yMyA5dC05IDIzdjE2MHEwIDE0IDkgMjN0MjMgOWg0NDhxMTQgMCAyMy05dDktMjN6bS0xMjgtODk2di0xNjBxMC0xNC05LTIzdC0yMy05aC0xOTJxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMtOXQ5LTIzem02NDAgNDE2cTAgMjA5LTEwMyAzODUuNXQtMjc5LjUgMjc5LjUtMzg1LjUgMTAzLTM4NS41LTEwMy0yNzkuNS0yNzkuNS0xMDMtMzg1LjUgMTAzLTM4NS41IDI3OS41LTI3OS41IDM4NS41LTEwMyAzODUuNSAxMDMgMjc5LjUgMjc5LjUgMTAzIDM4NS41eiIvPjwvc3ZnPg==); + } + + .icon-plus { + background-image: url(icon_plus_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHZpZXdCb3g9IjAgMCAxNzkyIDE3OTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI0JGQkZDMCIgZD0iTTE2MDAgNzM2djE5MnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtMTkycS00MCAwLTY4LTI4dC0yOC02OHYtNDE2aC00MTZxLTQwIDAtNjgtMjh0LTI4LTY4di0xOTJxMC00MCAyOC02OHQ2OC0yOGg0MTZ2LTQxNnEwLTQwIDI4LTY4dDY4LTI4aDE5MnE0MCAwIDY4IDI4dDI4IDY4djQxNmg0MTZxNDAgMCA2OCAyOHQyOCA2OHoiLz48L3N2Zz4=); + } + + .icon-minus { + background-image: url(icon_minus_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHZpZXdCb3g9IjAgMCAxNzkyIDE3OTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI0JGQkZDMCIgZD0iTTE2MDAgNzM2djE5MnEwIDQwLTI4IDY4dC02OCAyOGgtMTIxNnEtNDAgMC02OC0yOHQtMjgtNjh2LTE5MnEwLTQwIDI4LTY4dDY4LTI4aDEyMTZxNDAgMCA2OCAyOHQyOCA2OHoiLz48L3N2Zz4=); + } + + .icon-wrench { + background-image: url(icon_wrench_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHZpZXdCb3g9IjAgMCAxNzkyIDE3OTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI0JEQkRCRiIgZD0iTTQ0OCAxNDcycTAtMjYtMTktNDV0LTQ1LTE5LTQ1IDE5LTE5IDQ1IDE5IDQ1IDQ1IDE5IDQ1LTE5IDE5LTQ1em02NDQtNDIwbC02ODIgNjgycS0zNyAzNy05MCAzNy01MiAwLTkxLTM3bC0xMDYtMTA4cS0zOC0zNi0zOC05MCAwLTUzIDM4LTkxbDY4MS02ODFxMzkgOTggMTE0LjUgMTczLjV0MTczLjUgMTE0LjV6bTYzNC00MzVxMCAzOS0yMyAxMDYtNDcgMTM0LTE2NC41IDIxNy41dC0yNTguNSA4My41cS0xODUgMC0zMTYuNS0xMzEuNXQtMTMxLjUtMzE2LjUgMTMxLjUtMzE2LjUgMzE2LjUtMTMxLjVxNTggMCAxMjEuNSAxNi41dDEwNy41IDQ2LjVxMTYgMTEgMTYgMjh0LTE2IDI4bC0yOTMgMTY5djIyNGwxOTMgMTA3cTUtMyA3OS00OC41dDEzNS41LTgxIDcwLjUtMzUuNXExNSAwIDIzLjUgMTB0OC41IDI1eiIvPjwvc3ZnPg==); + } + + .icon-cog { + background-image: url(icon_cog_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjQkRCREJGIiBkPSJNNDQ0Ljc4OCAyOTEuMWw0Mi42MTYgMjQuNTk5YzQuODY3IDIuODA5IDcuMTI2IDguNjE4IDUuNDU5IDEzLjk4NS0xMS4wNyAzNS42NDItMjkuOTcgNjcuODQyLTU0LjY4OSA5NC41ODZhMTIuMDE2IDEyLjAxNiAwIDAgMS0xNC44MzIgMi4yNTRsLTQyLjU4NC0yNC41OTVhMTkxLjU3NyAxOTEuNTc3IDAgMCAxLTYwLjc1OSAzNS4xM3Y0OS4xODJhMTIuMDEgMTIuMDEgMCAwIDEtOS4zNzcgMTEuNzE4Yy0zNC45NTYgNy44NS03Mi40OTkgOC4yNTYtMTA5LjIxOS4wMDctNS40OS0xLjIzMy05LjQwMy02LjA5Ni05LjQwMy0xMS43MjN2LTQ5LjE4NGExOTEuNTU1IDE5MS41NTUgMCAwIDEtNjAuNzU5LTM1LjEzbC00Mi41ODQgMjQuNTk1YTEyLjAxNiAxMi4wMTYgMCAwIDEtMTQuODMyLTIuMjU0Yy0yNC43MTgtMjYuNzQ0LTQzLjYxOS01OC45NDQtNTQuNjg5LTk0LjU4Ni0xLjY2Ny01LjM2Ni41OTItMTEuMTc1IDUuNDU5LTEzLjk4NUw2Ny4yMTIgMjkxLjFhMTkzLjQ4IDE5My40OCAwIDAgMSAwLTcwLjE5OWwtNDIuNjE2LTI0LjU5OWMtNC44NjctMi44MDktNy4xMjYtOC42MTgtNS40NTktMTMuOTg1IDExLjA3LTM1LjY0MiAyOS45Ny02Ny44NDIgNTQuNjg5LTk0LjU4NmExMi4wMTYgMTIuMDE2IDAgMCAxIDE0LjgzMi0yLjI1NGw0Mi41ODQgMjQuNTk1YTE5MS41NzcgMTkxLjU3NyAwIDAgMSA2MC43NTktMzUuMTNWMjUuNzU5YTEyLjAxIDEyLjAxIDAgMCAxIDkuMzc3LTExLjcxOGMzNC45NTYtNy44NSA3Mi40OTktOC4yNTYgMTA5LjIxOS0uMDA3IDUuNDkgMS4yMzMgOS40MDMgNi4wOTYgOS40MDMgMTEuNzIzdjQ5LjE4NGExOTEuNTU1IDE5MS41NTUgMCAwIDEgNjAuNzU5IDM1LjEzbDQyLjU4NC0yNC41OTVhMTIuMDE2IDEyLjAxNiAwIDAgMSAxNC44MzIgMi4yNTRjMjQuNzE4IDI2Ljc0NCA0My42MTkgNTguOTQ0IDU0LjY4OSA5NC41ODYgMS42NjcgNS4zNjYtLjU5MiAxMS4xNzUtNS40NTkgMTMuOTg1TDQ0NC43ODggMjIwLjlhMTkzLjQ4NSAxOTMuNDg1IDAgMCAxIDAgNzAuMnpNMzM2IDI1NmMwLTQ0LjExMi0zNS44ODgtODAtODAtODBzLTgwIDM1Ljg4OC04MCA4MCAzNS44ODggODAgODAgODAgODAtMzUuODg4IDgwLTgweiIvPjwvc3ZnPg==); + } + + .icon-fork { + background-image: url(icon_fork_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHZpZXdCb3g9IjAgMCAxNzkyIDE3OTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI0JGQkZDMCIgZD0iTTY3MiAxNDcycTAtNDAtMjgtNjh0LTY4LTI4LTY4IDI4LTI4IDY4IDI4IDY4IDY4IDI4IDY4LTI4IDI4LTY4em0wLTExNTJxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTY0MCAxMjhxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTk2IDBxMCA1Mi0yNiA5Ni41dC03MCA2OS41cS0yIDI4Ny0yMjYgNDE0LTY3IDM4LTIwMyA4MS0xMjggNDAtMTY5LjUgNzF0LTQxLjUgMTAwdjI2cTQ0IDI1IDcwIDY5LjV0MjYgOTYuNXEwIDgwLTU2IDEzNnQtMTM2IDU2LTEzNi01Ni01Ni0xMzZxMC01MiAyNi05Ni41dDcwLTY5LjV2LTgyMHEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnEwIDUyLTI2IDk2LjV0LTcwIDY5LjV2NDk3cTU0LTI2IDE1NC01NyA1NS0xNyA4Ny41LTI5LjV0NzAuNS0zMSA1OS0zOS41IDQwLjUtNTEgMjgtNjkuNSA4LjUtOTEuNXEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnoiLz48L3N2Zz4=); + } + + .icon-cube { + background-image: url(icon_cube_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHZpZXdCb3g9IjAgMCAxNzkyIDE3OTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI0JGQkZDMCIgZD0iTTg5NiAxNjI5bDY0MC0zNDl2LTYzNmwtNjQwIDIzM3Y3NTJ6bS02NC04NjVsNjk4LTI1NC02OTgtMjU0LTY5OCAyNTR6bTgzMi0yNTJ2NzY4cTAgMzUtMTggNjV0LTQ5IDQ3bC03MDQgMzg0cS0yOCAxNi02MSAxNnQtNjEtMTZsLTcwNC0zODRxLTMxLTE3LTQ5LTQ3dC0xOC02NXYtNzY4cTAtNDAgMjMtNzN0NjEtNDdsNzA0LTI1NnEyMi04IDQ0LTh0NDQgOGw3MDQgMjU2cTM4IDE0IDYxIDQ3dDIzIDczeiIvPjwvc3ZnPg==); + } + + .icon-search-plus { + background-image: url(icon_search-plus_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHZpZXdCb3g9IjAgMCAxNzkyIDE3OTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI0JGQkZDMCIgZD0iTTEwODggODAwdjY0cTAgMTMtOS41IDIyLjV0LTIyLjUgOS41aC0yMjR2MjI0cTAgMTMtOS41IDIyLjV0LTIyLjUgOS41aC02NHEtMTMgMC0yMi41LTkuNXQtOS41LTIyLjV2LTIyNGgtMjI0cS0xMyAwLTIyLjUtOS41dC05LjUtMjIuNXYtNjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWgyMjR2LTIyNHEwLTEzIDkuNS0yMi41dDIyLjUtOS41aDY0cTEzIDAgMjIuNSA5LjV0OS41IDIyLjV2MjI0aDIyNHExMyAwIDIyLjUgOS41dDkuNSAyMi41em0xMjggMzJxMC0xODUtMTMxLjUtMzE2LjV0LTMxNi41LTEzMS41LTMxNi41IDEzMS41LTEzMS41IDMxNi41IDEzMS41IDMxNi41IDMxNi41IDEzMS41IDMxNi41LTEzMS41IDEzMS41LTMxNi41em01MTIgODMycTAgNTMtMzcuNSA5MC41dC05MC41IDM3LjVxLTU0IDAtOTAtMzhsLTM0My0zNDJxLTE3OSAxMjQtMzk5IDEyNC0xNDMgMC0yNzMuNS01NS41dC0yMjUtMTUwLTE1MC0yMjUtNTUuNS0yNzMuNSA1NS41LTI3My41IDE1MC0yMjUgMjI1LTE1MCAyNzMuNS01NS41IDI3My41IDU1LjUgMjI1IDE1MCAxNTAgMjI1IDU1LjUgMjczLjVxMCAyMjAtMTI0IDM5OWwzNDMgMzQzcTM3IDM3IDM3IDkweiIvPjwvc3ZnPg==); + } + + .icon-search-minus { + background-image: url(icon_search-minus_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHZpZXdCb3g9IjAgMCAxNzkyIDE3OTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI0JGQkZDMCIgZD0iTTEwODggODAwdjY0cTAgMTMtOS41IDIyLjV0LTIyLjUgOS41aC01NzZxLTEzIDAtMjIuNS05LjV0LTkuNS0yMi41di02NHEwLTEzIDkuNS0yMi41dDIyLjUtOS41aDU3NnExMyAwIDIyLjUgOS41dDkuNSAyMi41em0xMjggMzJxMC0xODUtMTMxLjUtMzE2LjV0LTMxNi41LTEzMS41LTMxNi41IDEzMS41LTEzMS41IDMxNi41IDEzMS41IDMxNi41IDMxNi41IDEzMS41IDMxNi41LTEzMS41IDEzMS41LTMxNi41em01MTIgODMycTAgNTMtMzcuNSA5MC41dC05MC41IDM3LjVxLTU0IDAtOTAtMzhsLTM0My0zNDJxLTE3OSAxMjQtMzk5IDEyNC0xNDMgMC0yNzMuNS01NS41dC0yMjUtMTUwLTE1MC0yMjUtNTUuNS0yNzMuNSA1NS41LTI3My41IDE1MC0yMjUgMjI1LTE1MCAyNzMuNS01NS41IDI3My41IDU1LjUgMjI1IDE1MCAxNTAgMjI1IDU1LjUgMjczLjVxMCAyMjAtMTI0IDM5OWwzNDMgMzQzcTM3IDM3IDM3IDkweiIvPjwvc3ZnPg==); + } + + .icon-star { + background-image: url(icon_star_dark.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNzI4IDY0N3EwIDIyLTI2IDQ4bC0zNjMgMzU0IDg2IDUwMHExIDcgMSAyMCAwIDIxLTEwLjUgMzUuNXQtMzAuNSAxNC41cS0xOSAwLTQwLTEybC00NDktMjM2LTQ0OSAyMzZxLTIyIDEyLTQwIDEyLTIxIDAtMzEuNS0xNC41dC0xMC41LTM1LjVxMC02IDItMjBsODYtNTAwLTM2NC0zNTRxLTI1LTI3LTI1LTQ4IDAtMzcgNTYtNDZsNTAyLTczIDIyNS00NTVxMTktNDEgNDktNDF0NDkgNDFsMjI1IDQ1NSA1MDIgNzNxNTYgOSA1NiA0NnoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=); + } + } +} + +.ct-double-octave:after,.ct-golden-section:after,.ct-major-eleventh:after,.ct-major-second:after,.ct-major-seventh:after,.ct-major-sixth:after,.ct-major-tenth:after,.ct-major-third:after,.ct-major-twelfth:after,.ct-minor-second:after,.ct-minor-seventh:after,.ct-minor-sixth:after,.ct-minor-third:after,.ct-octave:after,.ct-perfect-fifth:after,.ct-perfect-fourth:after,.ct-square:after{content:"";clear:both}.ct-label{fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-grid-background{fill:none}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{fill:none;stroke-width:4px}.ct-area{stroke:none;fill-opacity:.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{display:table}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{display:table}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{display:table}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{display:table}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{display:table}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{display:table}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{display:table}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{display:table}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{display:table}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{display:table}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{display:table}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{display:table}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{display:table}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{display:table}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{display:table}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{display:table}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{display:table}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0} \ No newline at end of file From b060468ff7befddce4dad8d12811761c3d7f5e0e Mon Sep 17 00:00:00 2001 From: alvaro13bq Date: Mon, 16 Feb 2026 12:29:58 +0100 Subject: [PATCH 3/5] final refactor --- .../DTO/ExchangeRateEntry.cs | 2 +- .../DTO/ExchangeRatesResponse.cs | 2 +- ...iClient.cs => ExchangeRateCnbApiClient.cs} | 4 +- .../Mapper/CnbExchangeRateMapperExtensions.cs | 2 +- .../DependencyInyection.md | 18 ++++--- .../ExchangeRateCnbServicesExtension.cs | 2 +- .../ExchangeRateProvider.cs | 50 ++++++++++++------- .../Api/ExchangeRateControllerTests.cs | 24 ++++----- .../CnbExchangeRateMapperExtensionsTests.cs | 2 +- .../ExchangeRateCbnApiClientTests.cs | 16 +++--- .../IntegrationTests/IntegrationTests.cs | 20 ++++---- .../Service/ExchangeRateProviderTests.cs | 20 ++++---- .../Service/ExchangeRateServiceTests.cs | 12 ++--- 13 files changed, 97 insertions(+), 77 deletions(-) rename jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/{ExchangeRateCbnApiClient.cs => ExchangeRateCnbApiClient.cs} (95%) diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRateEntry.cs b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRateEntry.cs index 01872fbc4b..8c1157864e 100644 --- a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRateEntry.cs +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRateEntry.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -namespace ExchangeRateUpdater.CbnApiClient.DTO +namespace ExchangeRateUpdater.CbnApiClient.Dto { /// /// Represents an individual exchange rate entry as returned by the CBN API. diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRatesResponse.cs b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRatesResponse.cs index dd2b866fbf..1760c82864 100644 --- a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRatesResponse.cs +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/DTO/ExchangeRatesResponse.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -namespace ExchangeRateUpdater.CbnApiClient.DTO +namespace ExchangeRateUpdater.CbnApiClient.Dto { /// /// Represents the response returned by the CBN API containing a collection of exchange rate entries. diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCbnApiClient.cs b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCnbApiClient.cs similarity index 95% rename from jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCbnApiClient.cs rename to jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCnbApiClient.cs index 08be3d441e..b4d03d1f05 100644 --- a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCbnApiClient.cs +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Implementation/ExchangeRateCnbApiClient.cs @@ -1,6 +1,6 @@ using ExchangeRateUpdater.Abstraction.Interfaces; using ExchangeRateUpdater.Abstraction.Model; -using ExchangeRateUpdater.CbnApiClient.DTO; +using ExchangeRateUpdater.CbnApiClient.Dto; using ExchangeRateUpdater.CbnApiClient.Mapper; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -8,7 +8,7 @@ namespace ExchangeRateUpdater.CbnApiClient.Implementation { - public class ExchangeRateCbnApiClient(IHttpClientFactory httpClientFactory, IConfiguration config, ILogger logger) : IExchangeRateClient + public class ExchangeRateCnbApiClient(IHttpClientFactory httpClientFactory, IConfiguration config, ILogger logger) : IExchangeRateClient { private readonly string CnbApiUrl = config.GetValue("CbnApiUrl") ?? throw new InvalidOperationException("CbnApiUrl configuration is missing."); // Supports EN and CZ, CZ is the default if not specified. diff --git a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Mapper/CnbExchangeRateMapperExtensions.cs b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Mapper/CnbExchangeRateMapperExtensions.cs index f10927119c..e62670f6a8 100644 --- a/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Mapper/CnbExchangeRateMapperExtensions.cs +++ b/jobs/Backend/ExchangeRateUpdater.CbnApiClient/Mapper/CnbExchangeRateMapperExtensions.cs @@ -1,4 +1,4 @@ -using ExchangeRateUpdater.CbnApiClient.DTO; +using ExchangeRateUpdater.CbnApiClient.Dto; using ExchangeRateUpdater.Abstraction.Model; namespace ExchangeRateUpdater.CbnApiClient.Mapper diff --git a/jobs/Backend/ExchangeRateUpdater.DependencyInjection/DependencyInyection.md b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/DependencyInyection.md index 2e49bbba16..56c2fcb72b 100644 --- a/jobs/Backend/ExchangeRateUpdater.DependencyInjection/DependencyInyection.md +++ b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/DependencyInyection.md @@ -1,16 +1,20 @@ # Dependency Injection Design + ## Purpose + This project encapsulates the dependency injection configuration for the exchange rate services. Instead of registering all services directly inside a specific host (for example, the Web API), the setup is extracted into a shared extension method that can be reused by multiple applications. -The main entry point is the AddExchangeRateServices(IServiceCollection services) extension method, which wires the IExchangeRateProvider implementation and the CNB HTTP client together with basic resilience policies. +The main entry point is the `AddExchangeRateServices(IServiceCollection services)` extension method, which wires together: + +- The `IExchangeRateProvider` and `IExchangeRateService` implementations. +- The CNB HTTP client (`IExchangeRateClient`) configured with a basic resilience policy (retries with exponential backoff). ## Why a separate project? -There are several reasons for keeping this registration logic in a dedicated project rather than placing it directly inside the API project: -- **Separation of concerns**\ -The host projects (API, console, etc.) focus on hosting concerns (endpoints, input/output, configuration), while this project focuses only on how the exchange rate domain and infrastructure are composed. This makes the startup code of each host simpler and easier to read. +There are several reasons for keeping this registration logic in a dedicated project rather than placing it directly inside the API project: -- **Future extensibility**\ -If in the future a new host is introduced (for example, a SOAP service, a Windows service, or a background worker), it can reuse the same AddExchangeRateServices method without needing to replicate the registration logic. +- **Separation of concerns** + The host projects (API, console, etc.) focus on hosting concerns (endpoints, input/output, configuration), while this project focuses only on how the exchange rate domain and infrastructure are composed. This makes the startup code of each host simpler and easier to read. -Centralizing this setup ensures that all consumers benefit from the same resilience configuration and that changes (for example, different retry settings or a different client implementation) can be made in a single place. \ No newline at end of file +- **Reusability across hosts** + Any application can call the same `AddExchangeRateServices` method to get a fully configured exchange rate stack without duplicating registration code. diff --git a/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateCnbServicesExtension.cs b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateCnbServicesExtension.cs index 5a4594113f..38c4f94e6f 100644 --- a/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateCnbServicesExtension.cs +++ b/jobs/Backend/ExchangeRateUpdater.DependencyInjection/ExchangeRateCnbServicesExtension.cs @@ -21,7 +21,7 @@ public static IServiceCollection AddExchangeRateServices(this IServiceCollection services.AddMemoryCache(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); // Configure a named HttpClient for the CNB API with Polly-based retry policy. // This will be available as httpClientFactory.CreateClient("CnbApi"). diff --git a/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateProvider.cs b/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateProvider.cs index f0617a6a51..e3d0bc8812 100644 --- a/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateProvider.cs +++ b/jobs/Backend/ExchangeRateUpdater.Service/ExchangeRateProvider.cs @@ -24,24 +24,9 @@ public async Task> GetExchangeRatesAsync(IEnumerable= nextPublication) - { - // already past today's publication time, schedule for next day - nextPublication = nextPublication.AddDays(1); - } - - var absoluteExpirationRelative = nextPublication - now; - // Guard: ensure a minimum cache duration and reasonable maximum (24h) - // Cache will refresh even on non-working days, but that's acceptable because 1 unnecessary request a day is not a problem for CNB. - if (absoluteExpirationRelative < TimeSpan.FromMinutes(1)) absoluteExpirationRelative = TimeSpan.FromMinutes(1); - if (absoluteExpirationRelative > TimeSpan.FromHours(24)) absoluteExpirationRelative = TimeSpan.FromHours(24); - + var cacheExpiration = CalculateCacheExpiration(); var options = new MemoryCacheEntryOptions() - .SetAbsoluteExpiration(absoluteExpirationRelative); + .SetAbsoluteExpiration(cacheExpiration); cache.Set(CacheKey, fetched, options); cachedRates = fetched; @@ -55,5 +40,36 @@ public async Task> GetExchangeRatesAsync(IEnumerable currencySet.Contains(rate.SourceCurrency)); return filteredRates; } + + /// + /// Calculates the cache expiration duration based on CNB API publication schedule. + /// The CNB publishes new rates around 15:00 UTC on working days. + /// This method schedules the cache to expire shortly after the next expected publication. + /// + /// A TimeSpan representing the cache duration (minimum 1 minute, maximum 24 hours). + private static TimeSpan CalculateCacheExpiration() + { + var now = DateTime.UtcNow; + var nextPublication = new DateTime(now.Year, now.Month, now.Day, 15, 0, 0, DateTimeKind.Utc); + + if (now >= nextPublication) + { + // Already past today's publication time, schedule for next day + nextPublication = nextPublication.AddDays(1); + } + + var absoluteExpirationRelative = nextPublication - now; + + // Guard: ensure a minimum cache duration and reasonable maximum (24h) + // Cache will refresh even on non-working days, but that's acceptable because + // 1 unnecessary request a day is not a problem for CNB. + if (absoluteExpirationRelative < TimeSpan.FromMinutes(1)) + absoluteExpirationRelative = TimeSpan.FromMinutes(1); + + if (absoluteExpirationRelative > TimeSpan.FromHours(24)) + absoluteExpirationRelative = TimeSpan.FromHours(24); + + return absoluteExpirationRelative; + } } } diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/Api/ExchangeRateControllerTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/Api/ExchangeRateControllerTests.cs index 4eab0f3984..356ee66804 100644 --- a/jobs/Backend/ExchangeRateUpdater.Tests/Api/ExchangeRateControllerTests.cs +++ b/jobs/Backend/ExchangeRateUpdater.Tests/Api/ExchangeRateControllerTests.cs @@ -10,16 +10,16 @@ namespace ExchangeRateUpdater.Tests.Api [TestFixture] internal class ExchangeRateControllerTests { - private IExchangeRateService _service = null!; - private ILogger _logger = null!; - private ExchangeRateController _controller = null!; + private IExchangeRateService service = null!; + private ILogger logger = null!; + private ExchangeRateController controller = null!; [SetUp] public void SetUp() { - _service = Substitute.For(); - _logger = Substitute.For>(); - _controller = new ExchangeRateController(_service, _logger); + service = Substitute.For(); + logger = Substitute.For>(); + controller = new ExchangeRateController(service, logger); } [Test] @@ -27,9 +27,9 @@ public async Task GetExchangeRates_ReturnsOkWithRates_WhenServiceReturnsRates() { var now = DateTime.UtcNow; var rates = new[] { new ExchangeRate(new Currency("USD"), new Currency("CZK"), 25m, now) }; - _service.GetExchangeRatesFromStringList(Arg.Any>()).Returns(Task.FromResult>(rates)); + service.GetExchangeRatesFromStringList(Arg.Any>()).Returns(Task.FromResult>(rates)); - var result = await _controller.GetExchangeRates("USD, EUR"); + var result = await controller.GetExchangeRates("USD, EUR"); Assert.That(result.Result, Is.TypeOf()); var ok = (OkObjectResult)result.Result!; @@ -39,22 +39,22 @@ public async Task GetExchangeRates_ReturnsOkWithRates_WhenServiceReturnsRates() Assert.That(returned[0].SourceCurrency.Code, Is.EqualTo("USD")); // Ensure service was called with the split and trimmed values - await _service.Received(1).GetExchangeRatesFromStringList(Arg.Is>(s => s.SequenceEqual(new[] { "USD", "EUR" }))); + await service.Received(1).GetExchangeRatesFromStringList(Arg.Is>(s => s.SequenceEqual(new[] { "USD", "EUR" }))); } [Test] public async Task GetExchangeRates_ReturnsBadRequest_WhenNoRates() { - _service.GetExchangeRatesFromStringList(Arg.Any>()).Returns(Task.FromResult>(Array.Empty())); + service.GetExchangeRatesFromStringList(Arg.Any>()).Returns(Task.FromResult>(Array.Empty())); - var result = await _controller.GetExchangeRates("USD"); + var result = await controller.GetExchangeRates("USD"); Assert.That(result.Result, Is.TypeOf()); var bad = (BadRequestObjectResult)result.Result!; Assert.That(bad.Value, Is.EqualTo("No exchange rates found for the provided currency codes.")); // Logger should have been called to warn - _logger.Received().Log( + logger.Received().Log( LogLevel.Warning, Arg.Any(), Arg.Any(), diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/CnbExchangeRateMapperExtensionsTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/CnbExchangeRateMapperExtensionsTests.cs index 9b4cb07e6f..bc33d87558 100644 --- a/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/CnbExchangeRateMapperExtensionsTests.cs +++ b/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/CnbExchangeRateMapperExtensionsTests.cs @@ -1,4 +1,4 @@ -using ExchangeRateUpdater.CbnApiClient.DTO; +using ExchangeRateUpdater.CbnApiClient.Dto; using ExchangeRateUpdater.CbnApiClient.Mapper; namespace ExchangeRateUpdater.Tests.CbnApiClient diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/ExchangeRateCbnApiClientTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/ExchangeRateCbnApiClientTests.cs index a688e3234d..96a0ffc58d 100644 --- a/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/ExchangeRateCbnApiClientTests.cs +++ b/jobs/Backend/ExchangeRateUpdater.Tests/CbnApiClient/ExchangeRateCbnApiClientTests.cs @@ -9,13 +9,13 @@ namespace ExchangeRateUpdater.Tests.CbnApiClient { public class ExchangeRateCbnApiClientTests { - private IConfiguration _config = null!; - private readonly ILogger _logger = NullLogger.Instance; + private IConfiguration config = null!; + private readonly ILogger logger = NullLogger.Instance; [SetUp] public void SetUp() { - _config = new ConfigurationBuilder() + config = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { { "CbnApiUrl", "https://api.cnb.cz/cnbapi/exrates/daily" } }) .Build(); } @@ -44,7 +44,7 @@ public async Task GetExchangeRatesAsync_ReturnsMappedRates() var factory = CreateHttpClientFactory(response); - var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + var client = new ExchangeRateCnbApiClient(factory, config, logger); var rates = await client.GetExchangeRatesAsync(); Assert.That(rates, Is.Not.Null); @@ -61,7 +61,7 @@ public void GetExchangeRatesAsync_ThrowsOnNonSuccess() var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); var factory = CreateHttpClientFactory(response); - var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + var client = new ExchangeRateCnbApiClient(factory, config, logger); Assert.That(async () => await client.GetExchangeRatesAsync(), Throws.InstanceOf()); } @@ -71,7 +71,7 @@ public void GetExchangeRatesAsync_ThrowsOnEmptyContent() var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(string.Empty) }; var factory = CreateHttpClientFactory(response); - var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + var client = new ExchangeRateCnbApiClient(factory, config, logger); Assert.That(async () => await client.GetExchangeRatesAsync(), Throws.InstanceOf()); } @@ -81,7 +81,7 @@ public void GetExchangeRatesAsync_ThrowsOnInvalidJson() var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("not json") }; var factory = CreateHttpClientFactory(response); - var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + var client = new ExchangeRateCnbApiClient(factory, config, logger); Assert.That(async () => await client.GetExchangeRatesAsync(), Throws.InstanceOf()); } @@ -93,7 +93,7 @@ public async Task GetExchangeRatesAsync_IncludesDateParameterWhenRequested() var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(json) }; var factory = CreateHttpClientFactory(response, req => captured = req); - var client = new ExchangeRateCbnApiClient(factory, _config, _logger); + var client = new ExchangeRateCnbApiClient(factory, config, logger); var date = new DateTime(2026, 02, 15); await client.GetExchangeRatesAsync(date); diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/IntegrationTests/IntegrationTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/IntegrationTests/IntegrationTests.cs index 98617d8b79..93593d1d3d 100644 --- a/jobs/Backend/ExchangeRateUpdater.Tests/IntegrationTests/IntegrationTests.cs +++ b/jobs/Backend/ExchangeRateUpdater.Tests/IntegrationTests/IntegrationTests.cs @@ -10,8 +10,8 @@ namespace ExchangeRateUpdater.Tests.IntegrationTests [TestFixture] internal class IntegrationTests { - private WebApplicationFactory _factory = null!; - private HttpClient _apiClient = null!; + private WebApplicationFactory factory = null!; + private HttpClient apiClient = null!; [SetUp] public void SetUp() @@ -19,7 +19,7 @@ public void SetUp() // Reset the call counter before each test MockCnbApiHandler.ResetCallCount(); - _factory = new WebApplicationFactory() + factory = new WebApplicationFactory() .WithWebHostBuilder(builder => { builder.ConfigureTestServices(services => @@ -36,21 +36,21 @@ public void SetUp() }); }); - _apiClient = _factory.CreateClient(); + apiClient = factory.CreateClient(); } [TearDown] public void TearDown() { - _apiClient?.Dispose(); - _factory?.Dispose(); + apiClient?.Dispose(); + factory?.Dispose(); } [Test] public async Task GetExchangeRates_EndToEnd_ReturnsFilteredRates() { // Call the actual API endpoint - var response = await _apiClient.GetAsync("/ExchangeRate?currencyCodes=USD,EUR"); + var response = await apiClient.GetAsync("/ExchangeRate?currencyCodes=USD,EUR"); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); @@ -69,7 +69,7 @@ public async Task GetExchangeRates_EndToEnd_ReturnsFilteredRates() public async Task GetExchangeRates_InvalidCurrencyCodes_ReturnsBadRequest() { // Request with invalid codes (e.g., empty, too short) - var response = await _apiClient.GetAsync("/ExchangeRate?currencyCodes=US,E"); + var response = await apiClient.GetAsync("/ExchangeRate?currencyCodes=US,E"); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest).Or.EqualTo(HttpStatusCode.OK)); } @@ -78,14 +78,14 @@ public async Task GetExchangeRates_InvalidCurrencyCodes_ReturnsBadRequest() public async Task GetExchangeRates_CachingWorks_OnlyOneUpstreamCall() { // First call - var response1 = await _apiClient.GetAsync("/ExchangeRate?currencyCodes=USD"); + var response1 = await apiClient.GetAsync("/ExchangeRate?currencyCodes=USD"); Assert.That(response1.StatusCode, Is.EqualTo(HttpStatusCode.OK)); // Verify one upstream call was made Assert.That(MockCnbApiHandler.CallCount, Is.EqualTo(1)); // Second call (should hit cache if enabled) - var response2 = await _apiClient.GetAsync("/ExchangeRate?currencyCodes=EUR"); + var response2 = await apiClient.GetAsync("/ExchangeRate?currencyCodes=EUR"); Assert.That(response2.StatusCode, Is.EqualTo(HttpStatusCode.OK)); // Verify still only one upstream call was made (second request hit cache) diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateProviderTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateProviderTests.cs index e052d09e7f..f2fbbec929 100644 --- a/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateProviderTests.cs +++ b/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateProviderTests.cs @@ -10,20 +10,20 @@ namespace ExchangeRateUpdater.Tests.Service [TestFixture] public class ExchangeRateProviderTests { - private IExchangeRateClient _client = null!; - private IMemoryCache _cache = null!; + private IExchangeRateClient client = null!; + private IMemoryCache cache = null!; [SetUp] public void SetUp() { - _client = Substitute.For(); - _cache = new MemoryCache(new MemoryCacheOptions()); + client = Substitute.For(); + cache = new MemoryCache(new MemoryCacheOptions()); } [TearDown] public void TearDown() { - _cache.Dispose(); + cache.Dispose(); } [Test] @@ -33,9 +33,9 @@ public async Task GetExchangeRatesAsync_FetchesFromClientAndCaches() var usd = new ExchangeRate(new Currency("USD"), new Currency("CZK"), 25m, now); var eur = new ExchangeRate(new Currency("EUR"), new Currency("CZK"), 27m, now); - _client.GetExchangeRatesAsync().Returns(Task.FromResult>(new[] { usd, eur })); + client.GetExchangeRatesAsync().Returns(Task.FromResult>(new[] { usd, eur })); - var provider = new ExchangeRateProvider(_client, NullLogger.Instance, _cache); + var provider = new ExchangeRateProvider(client, NullLogger.Instance, cache); var requested = new[] { new Currency("usd") }; @@ -46,11 +46,11 @@ public async Task GetExchangeRatesAsync_FetchesFromClientAndCaches() Assert.That(arr1[0].SourceCurrency.Code, Is.EqualTo("USD")); // Client should have been called once - await _client.Received(1).GetExchangeRatesAsync(); + await client.Received(1).GetExchangeRatesAsync(); // Call again: should hit cache and not call client again var secondCall = await provider.GetExchangeRatesAsync(requested); - await _client.Received(1).GetExchangeRatesAsync(); + await client.Received(1).GetExchangeRatesAsync(); var arr2 = secondCall.ToArray(); Assert.That(arr2.Length, Is.EqualTo(1)); Assert.That(arr2[0].SourceCurrency.Code, Is.EqualTo("USD")); @@ -59,7 +59,7 @@ public async Task GetExchangeRatesAsync_FetchesFromClientAndCaches() [Test] public void GetExchangeRatesAsync_NullCurrencies_Throws() { - var provider = new ExchangeRateProvider(_client, NullLogger.Instance, _cache); + var provider = new ExchangeRateProvider(client, NullLogger.Instance, cache); Assert.That(async () => await provider.GetExchangeRatesAsync(null!), Throws.TypeOf()); } diff --git a/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateServiceTests.cs b/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateServiceTests.cs index 9d94d195f5..2a4f1776bd 100644 --- a/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateServiceTests.cs +++ b/jobs/Backend/ExchangeRateUpdater.Tests/Service/ExchangeRateServiceTests.cs @@ -9,14 +9,14 @@ namespace ExchangeRateUpdater.Tests.Service [TestFixture] internal class ExchangeRateServiceTests { - private IExchangeRateProvider _provider = null!; - private ILogger _logger = null!; + private IExchangeRateProvider provider = null!; + private ILogger logger = null!; [SetUp] public void SetUp() { - _provider = Substitute.For(); - _logger = Substitute.For>(); + provider = Substitute.For(); + logger = Substitute.For>(); } [Test] @@ -25,10 +25,10 @@ public async Task GetExchangeRatesFromStringList_ValidCodes_CallsProviderAndRetu var now = DateTime.UtcNow; var expected = new[] { new ExchangeRate(new Currency("USD"), new Currency("CZK"), 25m, now) }; - _provider.GetExchangeRatesAsync(Arg.Is>(list => list.Select(c => c.Code).SequenceEqual(new[] { "USD" }))) + provider.GetExchangeRatesAsync(Arg.Is>(list => list.Select(c => c.Code).SequenceEqual(new[] { "USD" }))) .Returns(Task.FromResult>(expected)); - var svc = new ExchangeRateService(_provider, _logger); + var svc = new ExchangeRateService(provider, logger); var result = await svc.GetExchangeRatesFromStringList(new[] { "USD" }); Assert.That(result, Is.Not.Null); From 2c729dd99145ef85117686cd0bf5667f4751306f Mon Sep 17 00:00:00 2001 From: alvaro13bq Date: Mon, 16 Feb 2026 12:30:19 +0100 Subject: [PATCH 4/5] remove original project and update .md --- jobs/Backend/ArchitectureAndDecisions.md | 179 ++++++++++++++++++ jobs/Backend/Task/ArchitectureAndDecisions.md | 33 ---- jobs/Backend/Task/ExchangeRateUpdater.sln | 8 +- 3 files changed, 180 insertions(+), 40 deletions(-) create mode 100644 jobs/Backend/ArchitectureAndDecisions.md delete mode 100644 jobs/Backend/Task/ArchitectureAndDecisions.md diff --git a/jobs/Backend/ArchitectureAndDecisions.md b/jobs/Backend/ArchitectureAndDecisions.md new file mode 100644 index 0000000000..496ee18bee --- /dev/null +++ b/jobs/Backend/ArchitectureAndDecisions.md @@ -0,0 +1,179 @@ +# Architecture & Decisions + +## Context and goals + +This document describes the architecture and key decisions made while implementing an `ExchangeRateProvider` for the Czech National Bank (CNB). The goal is to build a small but production-ready set of components that focus on clarity, testability, and resilience rather than on feature completeness. The solution is structured so that the core abstractions and CNB-specific implementation can be reused from different hosts. + +## Target framework choice (.NET 10) + +The original skeleton project was provided targeting .NET 6. For the implementation, I updated the target framework to .NET 10 (`net10.0`) for the following reasons: + +- .NET 10 is a Long Term Support (LTS) release, supported for three years, which makes it a stable choice for production-oriented components. +- It provides the latest runtime and library improvements, including performance and reliability enhancements. + + +## Project structure + +The solution is split into several projects to keep responsibilities clear and to mirror a realistic architecture: + +- `ExchangeRateUpdater.Abstraction` + Contains the core domain model and contracts: + - `Model/` with `Currency` and `ExchangeRate` as strongly-typed value objects. + - `Interfaces/` with `IExchangeRateClient`, `IExchangeRateProvider`, and `IExchangeRateService`. + This project has no external dependencies other than the base class library. + +- `ExchangeRateUpdater.CbnApiClient` + Encapsulates the integration with the Czech National Bank API: + - `Dto/` contains transport-level models (`ExchangeRatesResponse`, `ExchangeRateEntry`) that match the CNB API payload. + - `Implementation/ExchangeRateCnbApiClient` implements `IExchangeRateClient` using `HttpClient`. + - `Mapper/CnbExchangeRateMapperExtensions` converts CNB DTOs into domain `ExchangeRate` instances. + The `CnbApiInfo.md` file documents the chosen endpoint, format, and behaviour of `validFor`. + +- `ExchangeRateUpdater.Service` + Contains the application-level services: + - `ExchangeRateProvider` implements `IExchangeRateProvider` on top of `IExchangeRateClient`. It applies domain rules, filtering, and caching on top of the raw CNB data. + - `ExchangeRateService` implements `IExchangeRateService` and acts as an application service that maps input (currency codes as strings) to domain types and orchestrates calls to the provider. + +- `ExchangeRateUpdater.Api` + Hosts an ASP.NET Core Web API: + - `Controllers/ExchangeRateController` exposes a HTTP endpoint to retrieve exchange rates for a set of currency codes. + - `Middleware/ExceptionHandlingMiddleware` centralizes exception handling and error responses. + - `Program.cs` wires the DI container, logging, and middleware pipeline. + The API is intentionally thin: it focuses on HTTP concerns and delegates business logic to `IExchangeRateService`. + +- `ExchangeRateUpdater.DependencyInjection` + Provides a reusable DI setup: + - `ExchangeRateCnbServicesExtension` exposes an `AddExchangeRateServices(IServiceCollection)` extension method that registers `IExchangeRateProvider`, `IExchangeRateService`, and the CNB HTTP client with resilience policies. + - `DependencyInyection.md` documents why this configuration is kept in a reusable project. + This allows both the API and a console application to share the same wiring without duplicating configuration. + +- `ExchangeRateUpdater.Tests` + Contains automated tests organized by area: + - `Abstraction/`, `Api/`, `CnbApiClient/`, `Service/` and `IntegrationTests/`. + Unit tests target domain classes, mapping logic, and provider behaviour; integration tests cover the end-to-end path through the service and API layers. + +## Data source (Czech National Bank) + +The provider uses an official public data source published by the Czech National Bank. The chosen endpoint returns the full daily set of foreign exchange rates, each entry including a `validFor` date that indicates when the rate applies. + +Relevant characteristics: + +- Rates are published on business days (weekdays) and updated once per day, around 14:30 UTC, according to CNB documentation. +- The endpoint always returns the full set of available currency entries; there is no server-side filtering by currency. +- When queried on weekends or holidays, the API returns the most recently published rates, i.e., the latest `validFor` date prior to the request. + +Because of these characteristics, the client always requests the full daily rates payload and leaves filtering and caching to upper layers. + +## Domain model and contracts + +The domain model is intentionally small and focused: + +- `Currency` is a value object that represents a three-letter ISO 4217 currency code. It enforces its invariants in the constructor: the code must be non-empty, exactly three characters long, and composed of letters only. The code is normalized using `ToUpperInvariant()`, and equality/hash code use a case-insensitive comparison to avoid culture-specific issues. +- `ExchangeRate` represents a rate between a base currency (CZK in this case), a quoted currency, and a validity date. + +Contracts are defined in the `Interfaces` namespace: + +- `IExchangeRateClient` abstracts the CNB integration. It is responsible for retrieving raw exchange rate data from the external API, but it does not apply business rules or filtering. +- `IExchangeRateProvider` represents a domain-facing abstraction that returns exchange rates for a given date and set of currencies. +- `IExchangeRateService` is an application-level service used by the API. It accepts currency codes as strings, maps them to `Currency` objects, and delegates to `IExchangeRateProvider`. + +This separation allows the Web API to depend only on abstractions, while the CNB-specific details are encapsulated in infrastructure projects. + +## Solution design + +The solution follows a layered design aligned with clean architecture principles: + +- **Domain (Abstraction project)** + Contains the core types and contracts with no external dependencies. + +- **Infrastructure (CbnApiClient project)** + Integrates with CNB using `HttpClient`. The client: + - Calls the official endpoint to fetch the full daily exchange rates payload. + - Deserializes the response into `ExchangeRatesResponse` and `ExchangeRateEntry`. + - Maps DTOs into domain-level `ExchangeRate` instances via `CnbExchangeRateMapperExtensions`. + +- **Application services (Service project)** + - `ExchangeRateProvider` uses `IExchangeRateClient` to retrieve the latest or requested daily rates. It then: + - Filters the full set to the requested currencies. + - Applies basic caching to avoid repeated calls for the same date. + - `ExchangeRateService` provides a higher-level API for hosts: it converts comma-separated currency codes into `Currency` objects and delegates the actual rate retrieval to the provider. + +- **Presentation (Api project)** + - `ExchangeRateController` is a thin Web API controller that: + - Accepts a `currencyCodes` query parameter. + - Splits and trims the codes. + - Calls `IExchangeRateService` and returns the resulting exchange rates. + - `ExceptionHandlingMiddleware` converts unhandled exceptions into consistent HTTP responses and logs them. + +- **Composition (DependencyInjection project)** + - `ExchangeRateCnbServicesExtension` centralizes DI registration so that multiple hosts can share the same wiring and resilience configuration. + +This separation of concerns keeps each project focused on a single responsibility, makes the code easier to test, and makes it clear where to make changes when integrating a different data source. + +## Error handling and resilience + +Error handling is applied at several levels: + +- **Domain validation** + `Currency` validates its input in the constructor and throws `ArgumentException` for invalid codes (null/whitespace, wrong length, or invalid characters). This ensures that any `Currency` instance used by the provider is always in a valid state. + +- **HTTP resilience (CNB client)** + The CNB HTTP client is registered via `AddHttpClient()` and configured with a Polly retry policy: + - Transient HTTP errors (5xx, timeouts, etc.) are retried up to three times. + - The wait times use exponential backoff (2, 4, and 8 seconds). + This provides basic resilience against temporary CNB outages or network glitches without adding complex logic to the client itself. + +- **API-level exception handling** + In the Web API project, `ExceptionHandlingMiddleware` catches unhandled exceptions, logs them, and returns standardized error responses. This avoids leaking implementation details to clients. + +- **Input validation at the API boundary** + The API controller validates and normalizes the `currencyCodes` query parameter (split, trim, ignore empty entries) before delegating to the service. + +## Caching strategy + +Given CNB’s update behaviour, caching is particularly effective for this use case. Daily FX rates are published once on business days around 14:30 UTC and remain valid for the entire day (and weekends/holidays until the next business day). The API always returns the latest available set of rates for the most recent `validFor` date. + +The `ExchangeRateProvider` uses an in-memory cache based on `IMemoryCache`. The cache stores the full set of CNB rates under a single key and applies time-based expiration: + +- When exchange rates are requested, the provider first checks the cache. +- On a cache miss, it calls `IExchangeRateClient` to fetch the latest daily rates from CNB and stores the result in the cache with an absolute expiration. +- On a cache hit, the provider simply filters the cached rates to the requested currencies, avoiding another HTTP call. + +The expiration is aligned with CNB’s daily publication schedule. The provider computes the next expected publication time (15:00 UTC with a small safety margin) and sets the cache entry to expire at that moment. This means: + +- For most of the day, repeated requests are served entirely from memory. +- Shortly after CNB publishes new rates, the next request will trigger a refresh and cache the new daily set. +- A minimum and maximum duration (between 1 minute and 24 hours) guard against edge cases in date/time calculations. + +This approach keeps the implementation simple while significantly reducing the number of external calls, which is a common recommendation for currency/exchange-rate APIs. + + +## Testing and validation + +Automated tests are an essential part of this solution. The goal is not only to demonstrate correctness for a few happy paths, but also to make the behaviour of the provider, services, and API predictable when the implementation evolves. + +The test suite covers domain types, CNB client mapping, caching behaviour, and the main API flow. External dependencies such as HTTP calls are abstracted behind interfaces (`IExchangeRateClient`), so tests run deterministically without hitting the real CNB endpoint. This makes it easy to validate edge cases (for example, unsupported currencies or malformed responses) in isolation. + +Code coverage is used as an additional feedback metric. A coverage report (`coveragereport/`) and a screenshot (`CodeCoverage.png`) are included at the root of the solution to make evaluation of the assignment easier. In a real production repository, these generated artefacts would typically not be committed, but produced by the build/CI pipeline instead. + +Most classes reach close to 100% coverage, and all core components have at least 85% coverage, ensuring that the critical paths of the system are exercised by tests. The only notable exception is `ExceptionHandlingMiddleware`, which currently has lower coverage as it mainly deals with cross-cutting concerns and framework integration, and is therefore less critical for the core domain logic. + + +## Maintainability and extensibility + +The design aims to make future changes as localized as possible: + +- Adding support for another bank or data source would typically involve: + - Introducing a new `IExchangeRateClient` implementation and mapper in a separate infrastructure project. + - Optionally adding another `IExchangeRateProvider` implementation if the semantics differ. +- The Web API depends only on abstractions (`IExchangeRateService`, `IExchangeRateProvider`, `IExchangeRateClient`), which makes it easier to extend or replace infrastructure without touching controllers. +- Configuration points such as base URLs, timeouts, and retry policies live in the DI setup and application configuration (`appsettings.json`), not in the core logic, so they can be adjusted without code changes. + +## Future improvements + +Potential future improvements include: + +- Introducing a more advanced caching strategy (e.g., configurable expiration or a distributed cache) if the service runs in a multi-instance environment. +- Exposing configuration for retry policies and resilience strategies (number of retries, backoff function) via configuration rather than hard-coding them. +- Adding richer logging and metrics around CNB requests (latency, failure rates, cache hit rates) to simplify diagnostics and capacity planning. +- Extending the API to support historical queries and additional CNB endpoints (for example, endpoints that allow querying by arbitrary date) as described in the CNB documentation. diff --git a/jobs/Backend/Task/ArchitectureAndDecisions.md b/jobs/Backend/Task/ArchitectureAndDecisions.md deleted file mode 100644 index e6ff96ff4a..0000000000 --- a/jobs/Backend/Task/ArchitectureAndDecisions.md +++ /dev/null @@ -1,33 +0,0 @@ -# Architecture & Decisions -Context and goals -This document describes the architecture and key decisions made while implementing the ExchangeRateProvider for the Czech National Bank (CNB). The goal is to build a small but production-ready component that focuses on clarity, testability, and resilience rather than on feature completeness. - -## Target framework choice (.NET 10) - -The original skeleton project was provided targeting .NET 6. For the implementation, I updated the target framework to .NET 10 (`net10.0`) for the following reasons: - -- .NET 10 is a Long Term Support (LTS) release, supported for three years, which makes it a stable choice for production-oriented components. -- It provides the latest runtime and library improvements, including performance and reliability enhancements that benefit long-running services and network-bound components such as an exchange rate provider. - - -## Data source (Czech National Bank) -The provider uses an official public data source published by the Czech National Bank. The chosen endpoint exposes daily exchange rates for multiple currencies against CZK and is suitable for server-side consumption. The implementation assumes the format to be stable and documented, and it includes basic validation to detect unexpected changes in the response structure. - -## Domain model and contracts -The implementation follows the interface and expectations defined in ExchangeRateProvider.cs. The primary responsibility of the provider is to deliver exchange rates for a given date and currency in a strongly-typed way. Any parsing or transport-specific details are kept internal so that consumers only see a simple, domain-focused API. - -## Solution design -The solution is structured around a clear separation of concerns. An HTTP client component is responsible for calling the CNB endpoint and retrieving the raw response. A parser component translates the CNB response format into domain objects that represent exchange rates. The ExchangeRateProvider orchestrates these pieces and exposes a simple method to obtain exchange rates for the rest of the system. This separation makes the code easier to test and maintain. - -## Error handling and resilience -The provider handles common failure scenarios explicitly, such as network errors, timeouts, invalid responses, or missing exchange rates. In these cases, it either throws meaningful exceptions or returns clearly defined results, depending on the contract. Input parameters (such as dates or currency codes) are validated early to avoid ambiguous behavior. Timeouts and basic guards are used to prevent the application from hanging when the external service is slow or unavailable. -​ - -## Testing and validation -The implementation is covered by unit tests focusing on the most critical logic: parsing of the CNB response, correct mapping of raw data to domain models, and behavior in error scenarios (for example, unsupported currencies or malformed responses). External dependencies such as HTTP calls are abstracted behind interfaces so tests can run deterministically without hitting the real CNB endpoint. - -## Maintainability and extensibility -The design aims to make future changes as localized as possible. Adding another bank or data source would typically require introducing a new provider implementation and possibly a new parser, without affecting existing consumers. Configuration points (such as base URLs or timeouts) are kept outside of the core logic so they can be adjusted without code changes. - -## Future improvements -Potential future improvements include caching daily exchange rates to reduce the number of external calls, exposing configuration for retry policies and resilience strategies, and adding support for additional CNB endpoints (for example, historical data or different formats). Another possible enhancement is richer logging around failures to simplify diagnostics in production environments. \ No newline at end of file diff --git a/jobs/Backend/Task/ExchangeRateUpdater.sln b/jobs/Backend/Task/ExchangeRateUpdater.sln index 267928f2d5..5fb81af470 100644 --- a/jobs/Backend/Task/ExchangeRateUpdater.sln +++ b/jobs/Backend/Task/ExchangeRateUpdater.sln @@ -3,11 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 VisualStudioVersion = 18.3.11505.172 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater", "ExchangeRateUpdater.csproj", "{7B2695D6-D24C-4460-A58E-A10F08550CE0}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Elementos de la solución", "Elementos de la solución", "{48B4E715-10EA-4BC9-A470-3772CDD8D796}" ProjectSection(SolutionItems) = preProject - ArchitectureAndDecisions.md = ArchitectureAndDecisions.md + ..\ArchitectureAndDecisions.md = ..\ArchitectureAndDecisions.md EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.Abstraction", "..\ExchangeRateUpdater.Abstraction\ExchangeRateUpdater.Abstraction.csproj", "{057474C6-1170-4F57-BBE0-6346DCFF15F3}" @@ -28,10 +26,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.Build.0 = Release|Any CPU {057474C6-1170-4F57-BBE0-6346DCFF15F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {057474C6-1170-4F57-BBE0-6346DCFF15F3}.Debug|Any CPU.Build.0 = Debug|Any CPU {057474C6-1170-4F57-BBE0-6346DCFF15F3}.Release|Any CPU.ActiveCfg = Release|Any CPU From 3446bef7e7b6359a8106a6b41c7f8c839dc670db Mon Sep 17 00:00:00 2001 From: alvaro13bq Date: Mon, 16 Feb 2026 12:38:42 +0100 Subject: [PATCH 5/5] typo --- jobs/Backend/ArchitectureAndDecisions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jobs/Backend/ArchitectureAndDecisions.md b/jobs/Backend/ArchitectureAndDecisions.md index 496ee18bee..dd1e58a48c 100644 --- a/jobs/Backend/ArchitectureAndDecisions.md +++ b/jobs/Backend/ArchitectureAndDecisions.md @@ -131,7 +131,7 @@ Error handling is applied at several levels: ## Caching strategy -Given CNB’s update behaviour, caching is particularly effective for this use case. Daily FX rates are published once on business days around 14:30 UTC and remain valid for the entire day (and weekends/holidays until the next business day). The API always returns the latest available set of rates for the most recent `validFor` date. +Given CNB's update behaviour, caching is particularly effective for this use case. Daily FX rates are published once on business days around 14:30 UTC and remain valid for the entire day (and weekends/holidays until the next business day). The API always returns the latest available set of rates for the most recent `validFor` date. The `ExchangeRateProvider` uses an in-memory cache based on `IMemoryCache`. The cache stores the full set of CNB rates under a single key and applies time-based expiration: