diff --git a/README.md b/README.md index ecb007204..58b7fa5d8 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ Migrations are not a part of the repository - they are ignored in `.gitignore`. - STS: - - `Skoruba.IdentityServer4.STS.Identity` - [Quickstart UI for the IdentityServer4 with Asp.Net Core Identity and EF Core storage](https://github.com/IdentityServer/IdentityServer4.Samples/tree/master/Quickstarts/Combined_AspId_and_EFStorage) + - `Skoruba.IdentityServer4.STS.Identity` - project that contains the instance of IdentityServer4 and combine these samples - [Quickstart UI for the IdentityServer4 with Asp.Net Core Identity and EF Core storage](https://github.com/IdentityServer/IdentityServer4.Samples/tree/master/Quickstarts/Combined_AspId_and_EFStorage) and [damienbod - IdentityServer4 and Identity template](https://github.com/damienbod/IdentityServer4AspNetCoreIdentityTemplate) - Admin UI: @@ -220,12 +220,14 @@ It is possible to define the configuration according the client type - by defaul - Api Scopes - Api Scope Claims - Api Secrets + - Api Properties **Identity Resources** - Actions: Add, Update, Remove - Entities: - Identity Claims + - Identity Properties ## Asp.Net Core Identity @@ -261,15 +263,17 @@ It is possible to define the configuration according the client type - by defaul - [x] English - [x] Chinese - [x] Russian +- [x] Manage profile +- [x] Password reset +- [x] Account linking +- [x] Two-Factor Authentication (2FA) +- [ ] User registration ### 1.1.0: - [ ] Add audit logs to track changes ([#61](https://github.com/skoruba/IdentityServer4.Admin/issues/61)) - [ ] Create a project template using dotnet CLI - `dotnet new template` - [ ] Second template: The administration of the IdentityServer4 (without Asp.Net Core Identity) ([#79](https://github.com/skoruba/IdentityServer4.Admin/issues/79)) -- [ ] User registration / Password reset -- [ ] Account linking -- [ ] Manage profile ### 2.0.0: diff --git a/src/Skoruba.IdentityServer4.Admin.EntityFramework.DbContexts/AdminDbContext.cs b/src/Skoruba.IdentityServer4.Admin.EntityFramework.DbContexts/AdminDbContext.cs index 54a2f4646..22669f68a 100644 --- a/src/Skoruba.IdentityServer4.Admin.EntityFramework.DbContexts/AdminDbContext.cs +++ b/src/Skoruba.IdentityServer4.Admin.EntityFramework.DbContexts/AdminDbContext.cs @@ -13,7 +13,7 @@ namespace Skoruba.IdentityServer4.Admin.EntityFramework.DbContexts { - public class AdminDbContext : IdentityDbContext, + public class AdminDbContext : IdentityDbContext, IAdminConfigurationDbContext, IAdminLogDbContext, IAdminPersistedGrantDbContext, IAdminPersistedGrantIdentityDbContext { private readonly ConfigurationStoreOptions _storeOptions; diff --git a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentity.cs b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentity.cs index a928b6b8e..c56e6eac8 100644 --- a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentity.cs +++ b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentity.cs @@ -2,7 +2,7 @@ namespace Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity { - public class UserIdentity : IdentityUser + public class UserIdentity : IdentityUser { } diff --git a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityClaim.cs b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityClaim.cs index 225ceb276..b70f51f8f 100644 --- a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityClaim.cs +++ b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityClaim.cs @@ -2,7 +2,7 @@ namespace Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity { - public class UserIdentityUserClaim : IdentityUserClaim + public class UserIdentityUserClaim : IdentityUserClaim { } } diff --git a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityRole.cs b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityRole.cs index df933eac5..1ca14e73f 100644 --- a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityRole.cs +++ b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityRole.cs @@ -2,7 +2,7 @@ namespace Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity { - public class UserIdentityRole : IdentityRole + public class UserIdentityRole : IdentityRole { } diff --git a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityRoleClaim.cs b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityRoleClaim.cs index dfbd008b6..e56f09e40 100644 --- a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityRoleClaim.cs +++ b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityRoleClaim.cs @@ -2,7 +2,7 @@ namespace Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity { - public class UserIdentityRoleClaim : IdentityRoleClaim + public class UserIdentityRoleClaim : IdentityRoleClaim { } diff --git a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserLogin.cs b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserLogin.cs index 6195b1059..17d6a43f5 100644 --- a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserLogin.cs +++ b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserLogin.cs @@ -2,7 +2,7 @@ namespace Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity { - public class UserIdentityUserLogin : IdentityUserLogin + public class UserIdentityUserLogin : IdentityUserLogin { } diff --git a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserRole.cs b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserRole.cs index 15d40547f..c4d0f580c 100644 --- a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserRole.cs +++ b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserRole.cs @@ -2,7 +2,7 @@ namespace Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity { - public class UserIdentityUserRole : IdentityUserRole + public class UserIdentityUserRole : IdentityUserRole { } diff --git a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserToken.cs b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserToken.cs index cb292a04b..da8ea6212 100644 --- a/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserToken.cs +++ b/src/Skoruba.IdentityServer4.Admin.EntityFramework.Identity/Entities/Identity/UserIdentityUserToken.cs @@ -2,7 +2,7 @@ namespace Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity { - public class UserIdentityUserToken : IdentityUserToken + public class UserIdentityUserToken : IdentityUserToken { } diff --git a/src/Skoruba.IdentityServer4.Admin/Controllers/GrantController.cs b/src/Skoruba.IdentityServer4.Admin/Controllers/GrantController.cs index 14a9a17be..a57e0d54d 100644 --- a/src/Skoruba.IdentityServer4.Admin/Controllers/GrantController.cs +++ b/src/Skoruba.IdentityServer4.Admin/Controllers/GrantController.cs @@ -17,10 +17,10 @@ namespace Skoruba.IdentityServer4.Admin.Controllers [TypeFilter(typeof(ControllerExceptionFilterAttribute))] public class GrantController : BaseController { - private readonly IPersistedGrantAspNetIdentityService _persistedGrantService; + private readonly IPersistedGrantAspNetIdentityService _persistedGrantService; private readonly IStringLocalizer _localizer; - public GrantController(IPersistedGrantAspNetIdentityService persistedGrantService, + public GrantController(IPersistedGrantAspNetIdentityService persistedGrantService, ILogger logger, IStringLocalizer localizer) : base(logger) { diff --git a/src/Skoruba.IdentityServer4.Admin/Controllers/IdentityController.cs b/src/Skoruba.IdentityServer4.Admin/Controllers/IdentityController.cs index bf418e480..d12f47e34 100644 --- a/src/Skoruba.IdentityServer4.Admin/Controllers/IdentityController.cs +++ b/src/Skoruba.IdentityServer4.Admin/Controllers/IdentityController.cs @@ -14,9 +14,9 @@ namespace Skoruba.IdentityServer4.Admin.Controllers { [Authorize(Policy = AuthorizationConsts.AdministrationPolicy)] [TypeFilter(typeof(ControllerExceptionFilterAttribute))] - public class IdentityController : BaseIdentityController, int, RoleDto, int, int, int, UserIdentity, UserIdentityRole, int, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken> + public class IdentityController : BaseIdentityController, string, RoleDto, string, string, string, UserIdentity, UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken> { - public IdentityController(IIdentityService, int, RoleDto, int, int, int, UserIdentity, UserIdentityRole, int, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken> identityService, ILogger logger, IStringLocalizer localizer) + public IdentityController(IIdentityService, string, RoleDto, string, string, string, UserIdentity, UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken> identityService, ILogger logger, IStringLocalizer localizer) : base(identityService, logger, localizer) { } diff --git a/src/Skoruba.IdentityServer4.Admin/Scripts/App/components/Picker.es5.js b/src/Skoruba.IdentityServer4.Admin/Scripts/App/components/Picker.es5.js index c48aa2e74..256c9c23f 100644 --- a/src/Skoruba.IdentityServer4.Admin/Scripts/App/components/Picker.es5.js +++ b/src/Skoruba.IdentityServer4.Admin/Scripts/App/components/Picker.es5.js @@ -1,201 +1,299 @@ -"use strict"; +'use strict'; + +ko.bindingHandlers.modal = { + init: function init(element, valueAccessor) { + $(element).modal({ + show: false + }); + + var value = valueAccessor(); + if (ko.isObservable(value)) { + $(element).on('hidden.bs.modal', function () { + value(false); + }); + } + }, + update: function update(element, valueAccessor) { + var value = valueAccessor(); + if (ko.utils.unwrapObservable(value)) { + $(element).modal('show'); + } else { + $(element).modal('hide'); + } + } +}; ko.components.register('picker', { - viewModel: function viewModel(params) { - - var self = this; - - //Constants - var minSearchSelectConst = 2; - var inputSearchDelay = 500; - var topSuggestedItemsConst = 5; - - //Input - this.textTerm = ko.observable("").extend({ rateLimit: inputSearchDelay }); - this.minSearchText = ko.observable(params.minSearchText || minSearchSelectConst); - this.multipleSelect = ko.observable(params.multipleSelect || false); - - //Labels - this.searchInputPlaceholder = ko.observable(params.searchInputPlaceholder || "Enter " + this.minSearchText() + " or more characters"); - this.selectedItemsTitle = ko.observable(params.selectedItemsTitle || "Selected: "); - this.searchResultTitle = ko.observable(params.searchResultTitle || "Search result: "); - this.suggestedItemsTitle = ko.observable(params.suggestedItemsTitle || "Suggested items: "); - - this.noItemSelectedTitle = ko.observable(params.noItemSelectedTitle || "No item/s selected"); - this.showAllItemsTitle = ko.observable(params.showAllItemsTitle || "more"); - this.allowSuggestedItems = ko.observable(params.allowSuggestedItems && params.url || false); - this.topSuggestedItems = ko.observable(params.topSuggestedItems || topSuggestedItemsConst); - this.allowItemAlreadySelectedNotification = ko.observable(params.allowItemAlreadySelectedNotification || true); - this.itemAlreadySelectedTitle = ko.observable(params.itemAlreadySelectedTitle || "item already selected"); - - //Collections - this.searchResult = ko.observableArray([]); - this.selectedResult = ko.observableArray(params.selectedItems || []); - this.suggestedResult = ko.observableArray([]); - - //Features - this.loading = ko.observable(false); - - //Sync selected items to hiddenField for server-side - var selectedItems = ko.toJSON(this.selectedResult); - if (this.multipleSelect() === true) { - if (this.selectedResult().length === 0) { - $("#" + params.hiddenId).val(""); - } else { - $("#" + params.hiddenId).val(selectedItems); - } - } else { - if (this.selectedResult().length === 0) { - $("#" + params.hiddenId).val(""); - } else { - $("#" + params.hiddenId).val(this.selectedResult()[0]); - } - } - - //Track changes on search input - this.textTerm.subscribe(function (searchTerm) { - - //If is search input clear -> clear search result - if (searchTerm.trim() === "") { - self.searchResult([]); - } - - //If search term isn't empty and has min length characters - if (searchTerm.trim() !== "" && searchTerm.trim().length >= self.minSearchText()) { - - if (params.url) { - //start loading - self.loading(true); - - //make ajax request and result add to search result - $.get(params.url + "=" + searchTerm, function (data) { - - if (data.indexOf(searchTerm) === -1) { - data.push(searchTerm); - } - - self.searchResult(data); - - //stop loading - self.loading(false); - }); - } else { - self.searchResult([searchTerm]); - } - } - }); - - this.notify = function (item) { - toastr.options.closeButton = true; - toastr.options.preventDuplicates = true; - toastr.info(item + " " + this.itemAlreadySelectedTitle()); - }; - - //Action methods - this.add = function (item) { - - //Replace quotes - item = item.replace(/'/g, "").replace(/"/g, ""); - - //Check if selected item is exists in selected items array - if (this.selectedResult.indexOf(item) > -1) { - - if (this.allowItemAlreadySelectedNotification() === true) { - this.notify(item); - } - - return; - } - - //Single select -> the original value replace with new - if (this.multipleSelect() === false) { - this.selectedResult([]); - this.selectedResult.push(item); - this.clear(); - this.sync(); - } - //Multiple select - else if (this.multipleSelect() === true) { - this.selectedResult.push(item); - this.clear(); - this.sync(); - } - }; - - //Get suggested items - by default 5 items - this.getSuggestedItems = function () { - - if (self.allowSuggestedItems() === false) return; - - if (params.url) { - //start loading - self.loading(true); - - //make ajax request and result add to suggested result - $.get(params.url, { - limit: self.topSuggestedItems() - }, function (data) { - - self.suggestedResult(data); - - //stop loading - self.loading(false); - }); - } - }; - - //Clear search input and search result - this.clear = function () { - this.textTerm(""); - self.searchResult([]); - }; - - //Remove selected item - this.remove = function (item) { - this.selectedResult.remove(item); - this.sync(); - }; - - //Show all items into suggested items - this.showAll = function () { - - if (params.url) { - //start loading - self.loading(true); - - //make ajax request and result add to search result - $.get("" + params.url, function (data) { - - self.suggestedResult(data); - - //stop loading - self.loading(false); - }); - } - }; - - //Synchronize the selected items to hidden field for server-side part - this.sync = function () { - var selectedItems = ko.toJSON(this.selectedResult); - if (this.multipleSelect() === true) { - if (this.selectedResult().length === 0) { - $("#" + params.hiddenId).val(""); - } else { - $("#" + params.hiddenId).val(selectedItems); - } - } else { - if (this.selectedResult().length === 0) { - $("#" + params.hiddenId).val(""); - } else { - $("#" + params.hiddenId).val(this.selectedResult()[0]); - } - } - }; - - //Load suggested items - self.getSuggestedItems(); - }, - template: '
' + '
' + '' + '
' + '
' + '
' + 'Loading..' + '
' + '
' + '
' + '
' + '
' + '
' + '' + '' + '' + '' + '
' + '
' + viewModel: function viewModel(params) { + + var self = this; + + //Constants + var minSearchSelectConst = 2; + var inputSearchDelay = 500; + var topSuggestedItemsConst = 5; + + //Input + this.textTerm = ko.observable("").extend({ rateLimit: inputSearchDelay }); + this.minSearchText = ko.observable(params.minSearchText || minSearchSelectConst); + this.multipleSelect = ko.observable(params.multipleSelect || false); + + //Labels + this.searchInputPlaceholder = ko.observable(params.searchInputPlaceholder || 'Enter ' + this.minSearchText() + ' or more characters'); + this.selectedItemsTitle = ko.observable(params.selectedItemsTitle || "Selected: "); + this.searchResultTitle = ko.observable(params.searchResultTitle || "Search result: "); + this.suggestedItemsTitle = ko.observable(params.suggestedItemsTitle || "Suggested items: "); + + this.noItemSelectedTitle = ko.observable(params.noItemSelectedTitle || "No item/s selected"); + this.showAllItemsTitle = ko.observable(params.showAllItemsTitle || "more"); + this.allowSuggestedItems = ko.observable(params.allowSuggestedItems && params.url || false); + this.topSuggestedItems = ko.observable(params.topSuggestedItems || topSuggestedItemsConst); + this.allowItemAlreadySelectedNotification = ko.observable(params.allowItemAlreadySelectedNotification || true); + this.itemAlreadySelectedTitle = ko.observable(params.itemAlreadySelectedTitle || "item already selected"); + + //Collections + this.searchResult = ko.observableArray([]); + this.selectedResult = ko.observableArray(params.selectedItems || []); + this.suggestedResult = ko.observableArray([]); + + //Features + this.loading = ko.observable(false); + + //Setup values for editing mode + //Value for dialog + this.isVisibleEditDialog = ko.observable(false); + + //In this value will be stored new value during editing mode + this.editedItem = ko.observable(""); + + //In this value will be stored original value during editing mode + this.editedItemOriginal = ko.observable(""); + + //Sync selected items to hiddenField for server-side + var selectedItems = ko.toJSON(this.selectedResult); + if (this.multipleSelect() === true) { + if (this.selectedResult().length === 0) { + $('#' + params.hiddenId).val(""); + } else { + $('#' + params.hiddenId).val(selectedItems); + } + } else { + if (this.selectedResult().length === 0) { + $('#' + params.hiddenId).val(""); + } else { + $('#' + params.hiddenId).val(this.selectedResult()[0]); + } + } + + //Track changes on search input + this.textTerm.subscribe(function (searchTerm) { + + //If is search input clear -> clear search result + if (searchTerm.trim() === "") { + self.searchResult([]); + } + + //If search term isn't empty and has min length characters + if (searchTerm.trim() !== "" && searchTerm.trim().length >= self.minSearchText()) { + + if (params.url) { + //start loading + self.loading(true); + + //make ajax request and result add to search result + $.get(params.url + '=' + searchTerm, function (data) { + + if (data.indexOf(searchTerm) === -1) { + data.push(searchTerm); + } + + self.searchResult(data); + + //stop loading + self.loading(false); + }); + } else { + self.searchResult([searchTerm]); + } + } + }); + + this.notify = function (item) { + toastr.options.closeButton = true; + toastr.options.preventDuplicates = true; + toastr.info(item + ' ' + this.itemAlreadySelectedTitle()); + }; + + this.notifyError = function (error) { + toastr.options.closeButton = true; + toastr.options.preventDuplicates = true; + toastr.error(error); + }; + + //Action methods + this.add = function (item) { + + //Replace quotes + item = item.replace(/'/g, "").replace(/"/g, ""); + + //Check if selected item is exists in selected items array + if (this.selectedResult.indexOf(item) > -1) { + + if (this.allowItemAlreadySelectedNotification() === true) { + this.notify(item); + } + + return; + } + + //Single select -> the original value replace with new + if (this.multipleSelect() === false) { + this.selectedResult([]); + this.selectedResult.push(item); + this.clear(); + this.sync(); + } + //Multiple select + else if (this.multipleSelect() === true) { + this.selectedResult.push(item); + this.clear(); + this.sync(); + } + }; + + //Get suggested items - by default 5 items + this.getSuggestedItems = function () { + + if (self.allowSuggestedItems() === false) return; + + if (params.url) { + //start loading + self.loading(true); + + //make ajax request and result add to suggested result + $.get(params.url, { + limit: self.topSuggestedItems() + }, function (data) { + + self.suggestedResult(data); + + //stop loading + self.loading(false); + }); + } + }; + + //Clear search input and search result + this.clear = function () { + this.textTerm(""); + self.searchResult([]); + }; + + //Show dialog for editing items + this.showEditDialog = function (item) { + //Setup value for showing the dialog + self.isVisibleEditDialog(true); + + //Setup values for editing + self.editedItem(item); + self.editedItemOriginal(item); + }; + + //Save item after editing + this.submitEditDialog = function () { + + //If is editing item empty + if (self.editedItem().trim() === "") { + return; + } + + //If item already exists show error message + if (self.checkIfItemExists(self.editedItemOriginal().trim(), self.editedItem().trim())) { + self.notifyError(self.editedItem().trim() + ' ' + this.itemAlreadySelectedTitle()); + return; + } + + //Update old value with new one + self.update(self.editedItemOriginal().trim(), self.editedItem().trim()); + + //Hide the dialog for editing + self.isVisibleEditDialog(false); + }; + + //Check if item which is editing is already selected + this.checkIfItemExists = function (item, newValue) { + + //The original item is same like new item + if (item.trim() === newValue.trim()) { + return false; + } + + //Item is already selected + if (this.selectedResult.indexOf(newValue) > -1) { + return true; + } + + return false; + }; + + //Update selected result + this.update = function (item, newValue) { + + for (var i = 0; i < self.selectedResult().length; i++) { + if (self.selectedResult()[i] === item) { + self.selectedResult()[i] = newValue; + self.selectedResult.valueHasMutated(); + break; + } + } + }; + + //Remove selected item + this.remove = function (item) { + this.selectedResult.remove(item); + this.sync(); + }; + + //Show all items into suggested items + this.showAll = function () { + + if (params.url) { + //start loading + self.loading(true); + + //make ajax request and result add to search result + $.get('' + params.url, function (data) { + + self.suggestedResult(data); + + //stop loading + self.loading(false); + }); + } + }; + + //Synchronize the selected items to hidden field for server-side part + this.sync = function () { + var selectedItems = ko.toJSON(this.selectedResult); + if (this.multipleSelect() === true) { + if (this.selectedResult().length === 0) { + $('#' + params.hiddenId).val(""); + } else { + $('#' + params.hiddenId).val(selectedItems); + } + } else { + if (this.selectedResult().length === 0) { + $('#' + params.hiddenId).val(""); + } else { + $('#' + params.hiddenId).val(this.selectedResult()[0]); + } + } + }; + + //Load suggested items + self.getSuggestedItems(); + }, + template: '
' + '
' + '' + '
' + '
' + '
' + 'Loading..' + '
' + '
' + '
' + '
' + '
' + '' + '' + '' + '
' + '
' + '
' + '
' + '' + '' + '' + '' + '
' + '
' + '' }); ko.applyBindings(); diff --git a/src/Skoruba.IdentityServer4.Admin/Scripts/App/components/Picker.es5.min.js b/src/Skoruba.IdentityServer4.Admin/Scripts/App/components/Picker.es5.min.js index 06e1a01ca..60570fd84 100644 --- a/src/Skoruba.IdentityServer4.Admin/Scripts/App/components/Picker.es5.min.js +++ b/src/Skoruba.IdentityServer4.Admin/Scripts/App/components/Picker.es5.min.js @@ -1 +1 @@ -"use strict";ko.components.register("picker",{viewModel:function(n){var t=this,i;this.textTerm=ko.observable("").extend({rateLimit:500});this.minSearchText=ko.observable(n.minSearchText||2);this.multipleSelect=ko.observable(n.multipleSelect||!1);this.searchInputPlaceholder=ko.observable(n.searchInputPlaceholder||"Enter "+this.minSearchText()+" or more characters");this.selectedItemsTitle=ko.observable(n.selectedItemsTitle||"Selected: ");this.searchResultTitle=ko.observable(n.searchResultTitle||"Search result: ");this.suggestedItemsTitle=ko.observable(n.suggestedItemsTitle||"Suggested items: ");this.noItemSelectedTitle=ko.observable(n.noItemSelectedTitle||"No item/s selected");this.showAllItemsTitle=ko.observable(n.showAllItemsTitle||"more");this.allowSuggestedItems=ko.observable(n.allowSuggestedItems&&n.url||!1);this.topSuggestedItems=ko.observable(n.topSuggestedItems||5);this.allowItemAlreadySelectedNotification=ko.observable(n.allowItemAlreadySelectedNotification||!0);this.itemAlreadySelectedTitle=ko.observable(n.itemAlreadySelectedTitle||"item already selected");this.searchResult=ko.observableArray([]);this.selectedResult=ko.observableArray(n.selectedItems||[]);this.suggestedResult=ko.observableArray([]);this.loading=ko.observable(!1);i=ko.toJSON(this.selectedResult);this.multipleSelect()===!0?this.selectedResult().length===0?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(i):this.selectedResult().length===0?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(this.selectedResult()[0]);this.textTerm.subscribe(function(i){i.trim()===""&&t.searchResult([]);i.trim()!==""&&i.trim().length>=t.minSearchText()&&(n.url?(t.loading(!0),$.get(n.url+"="+i,function(n){n.indexOf(i)===-1&&n.push(i);t.searchResult(n);t.loading(!1)})):t.searchResult([i]))});this.notify=function(n){toastr.options.closeButton=!0;toastr.options.preventDuplicates=!0;toastr.info(n+" "+this.itemAlreadySelectedTitle())};this.add=function(n){if(n=n.replace(/'/g,"").replace(/"/g,""),this.selectedResult.indexOf(n)>-1){this.allowItemAlreadySelectedNotification()===!0&&this.notify(n);return}this.multipleSelect()===!1?(this.selectedResult([]),this.selectedResult.push(n),this.clear(),this.sync()):this.multipleSelect()===!0&&(this.selectedResult.push(n),this.clear(),this.sync())};this.getSuggestedItems=function(){t.allowSuggestedItems()!==!1&&n.url&&(t.loading(!0),$.get(n.url,{limit:t.topSuggestedItems()},function(n){t.suggestedResult(n);t.loading(!1)}))};this.clear=function(){this.textTerm("");t.searchResult([])};this.remove=function(n){this.selectedResult.remove(n);this.sync()};this.showAll=function(){n.url&&(t.loading(!0),$.get(""+n.url,function(n){t.suggestedResult(n);t.loading(!1)}))};this.sync=function(){var t=ko.toJSON(this.selectedResult);this.multipleSelect()===!0?this.selectedResult().length===0?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(t):this.selectedResult().length===0?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(this.selectedResult()[0])};t.getSuggestedItems()},template:'

<\/div>
<\/div>Loading..
<\/div>
<\/div>
' + - - '
' + - '
' + - '' + - '' + - '' + - '' + - '
' + - '
' +ko.bindingHandlers.modal = { + init: function (element, valueAccessor) { + $(element).modal({ + show: false + }); + + var value = valueAccessor(); + if (ko.isObservable(value)) { + $(element).on('hidden.bs.modal', function () { + value(false); + }); + } + + }, + update: function (element, valueAccessor) { + var value = valueAccessor(); + if (ko.utils.unwrapObservable(value)) { + $(element).modal('show'); + } else { + $(element).modal('hide'); + } + } +} + +ko.components.register('picker', { + viewModel: function (params) { + + var self = this; + + //Constants + const minSearchSelectConst = 2; + const inputSearchDelay = 500; + const topSuggestedItemsConst = 5; + + //Input + this.textTerm = ko.observable("").extend({ rateLimit: inputSearchDelay }); + this.minSearchText = ko.observable(params.minSearchText || minSearchSelectConst); + this.multipleSelect = ko.observable(params.multipleSelect || false); + + //Labels + this.searchInputPlaceholder = ko.observable(params.searchInputPlaceholder || `Enter ${this.minSearchText()} or more characters`); + this.selectedItemsTitle = ko.observable(params.selectedItemsTitle || "Selected: "); + this.searchResultTitle = ko.observable(params.searchResultTitle || "Search result: "); + this.suggestedItemsTitle = ko.observable(params.suggestedItemsTitle || "Suggested items: "); + + this.noItemSelectedTitle = ko.observable(params.noItemSelectedTitle || "No item/s selected"); + this.showAllItemsTitle = ko.observable(params.showAllItemsTitle || "more"); + this.allowSuggestedItems = ko.observable((params.allowSuggestedItems && params.url) || false); + this.topSuggestedItems = ko.observable(params.topSuggestedItems || topSuggestedItemsConst); + this.allowItemAlreadySelectedNotification = ko.observable(params.allowItemAlreadySelectedNotification || true); + this.itemAlreadySelectedTitle = ko.observable(params.itemAlreadySelectedTitle || "item already selected"); + + //Collections + this.searchResult = ko.observableArray([]); + this.selectedResult = ko.observableArray(params.selectedItems || []); + this.suggestedResult = ko.observableArray([]); + + //Features + this.loading = ko.observable(false); + + //Setup values for editing mode + //Value for dialog + this.isVisibleEditDialog = ko.observable(false); + + //In this value will be stored new value during editing mode + this.editedItem = ko.observable(""); + + //In this value will be stored original value during editing mode + this.editedItemOriginal = ko.observable(""); + + //Sync selected items to hiddenField for server-side + const selectedItems = ko.toJSON(this.selectedResult); + if (this.multipleSelect() === true) { + if (this.selectedResult().length === 0) { + $(`#${params.hiddenId}`).val(""); + } else { + $(`#${params.hiddenId}`).val(selectedItems); + } + } else { + if (this.selectedResult().length === 0) { + $(`#${params.hiddenId}`).val(""); + } else { + $(`#${params.hiddenId}`).val(this.selectedResult()[0]); + } + } + + //Track changes on search input + this.textTerm.subscribe(function (searchTerm) { + + //If is search input clear -> clear search result + if (searchTerm.trim() === "") { + self.searchResult([]); + } + + //If search term isn't empty and has min length characters + if (searchTerm.trim() !== "" && searchTerm.trim().length >= self.minSearchText()) { + + if (params.url) { + //start loading + self.loading(true); + + //make ajax request and result add to search result + $.get(`${params.url}=${searchTerm}`, + function (data) { + + if (data.indexOf(searchTerm) === -1) { + data.push(searchTerm); + } + + self.searchResult(data); + + //stop loading + self.loading(false); + }); + } else { + self.searchResult([searchTerm]); + } + } + }); + + this.notify = function (item) { + toastr.options.closeButton = true; + toastr.options.preventDuplicates = true; + toastr.info(`${item} ${this.itemAlreadySelectedTitle()}`); + } + + this.notifyError = function (error) { + toastr.options.closeButton = true; + toastr.options.preventDuplicates = true; + toastr.error(error); + } + + //Action methods + this.add = function (item) { + + //Replace quotes + item = item.replace(/'/g, "").replace(/"/g, ""); + + //Check if selected item is exists in selected items array + if (this.selectedResult.indexOf(item) > -1) { + + if (this.allowItemAlreadySelectedNotification() === true) { + this.notify(item); + } + + return; + } + + //Single select -> the original value replace with new + if (this.multipleSelect() === false) { + this.selectedResult([]); + this.selectedResult.push(item); + this.clear(); + this.sync(); + } + //Multiple select + else if (this.multipleSelect() === true) { + this.selectedResult.push(item); + this.clear(); + this.sync(); + } + } + + //Get suggested items - by default 5 items + this.getSuggestedItems = function () { + + if (self.allowSuggestedItems() === false) return; + + if (params.url) { + //start loading + self.loading(true); + + //make ajax request and result add to suggested result + $.get(params.url, + { + limit: self.topSuggestedItems() + }, + function (data) { + + self.suggestedResult(data); + + //stop loading + self.loading(false); + }); + } + } + + //Clear search input and search result + this.clear = function () { + this.textTerm(""); + self.searchResult([]); + } + + //Show dialog for editing items + this.showEditDialog = function (item) { + //Setup value for showing the dialog + self.isVisibleEditDialog(true); + + //Setup values for editing + self.editedItem(item); + self.editedItemOriginal(item); + } + + //Save item after editing + this.submitEditDialog = function () { + + //If is editing item empty + if (self.editedItem().trim() === "") { + return; + } + + //If item already exists show error message + if (self.checkIfItemExists(self.editedItemOriginal().trim(), self.editedItem().trim())) { + self.notifyError(`${self.editedItem().trim()} ${this.itemAlreadySelectedTitle()}`); + return; + } + + //Update old value with new one + self.update(self.editedItemOriginal().trim(), self.editedItem().trim()); + + //Hide the dialog for editing + self.isVisibleEditDialog(false); + } + + //Check if item which is editing is already selected + this.checkIfItemExists = function (item, newValue) { + + //The original item is same like new item + if (item.trim() === newValue.trim()) { + return false; + } + + //Item is already selected + if (this.selectedResult.indexOf(newValue) > -1) { + return true; + } + + return false; + } + + //Update selected result + this.update = function (item, newValue) { + + for (var i = 0; i < self.selectedResult().length; i++) { + if (self.selectedResult()[i] === item) { + self.selectedResult()[i] = newValue; + self.selectedResult.valueHasMutated(); + break; + } + } + } + + //Remove selected item + this.remove = function (item) { + this.selectedResult.remove(item); + this.sync(); + } + + //Show all items into suggested items + this.showAll = function () { + + if (params.url) { + //start loading + self.loading(true); + + //make ajax request and result add to search result + $.get(`${params.url}`, + function (data) { + + self.suggestedResult(data); + + //stop loading + self.loading(false); + }); + } + } + + //Synchronize the selected items to hidden field for server-side part + this.sync = function () { + const selectedItems = ko.toJSON(this.selectedResult); + if (this.multipleSelect() === true) { + if (this.selectedResult().length === 0) { + $(`#${params.hiddenId}`).val(""); + } else { + $(`#${params.hiddenId}`).val(selectedItems); + } + } else { + if (this.selectedResult().length === 0) { + $(`#${params.hiddenId}`).val(""); + } else { + $(`#${params.hiddenId}`).val(this.selectedResult()[0]); + } + } + } + + //Load suggested items + self.getSuggestedItems(); + }, + template: '
' + + '
' + + '' + + '
' + + '
' + + '
' + + 'Loading..' + + '
' + + '
' + + '
' + + '
' + + '
' + + '' + + '' + + '' + + '
' + + '
' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '
' + + '
' + + '' }); ko.applyBindings(); \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.Admin/Startup.cs b/src/Skoruba.IdentityServer4.Admin/Startup.cs index 9ed73bb76..4fba8ce81 100644 --- a/src/Skoruba.IdentityServer4.Admin/Startup.cs +++ b/src/Skoruba.IdentityServer4.Admin/Startup.cs @@ -51,8 +51,8 @@ public void ConfigureServices(IServiceCollection services) services.AddAdminServices(); - services.AddAdminAspNetIdentityServices, int, RoleDto, int, int, int, - UserIdentity, UserIdentityRole, int, UserIdentityUserClaim, UserIdentityUserRole, + services.AddAdminAspNetIdentityServices, string, RoleDto, string, string, string, + UserIdentity, UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken>(); services.AddMvcLocalization(); diff --git a/src/Skoruba.IdentityServer4.Admin/Styles/controls/picker.scss b/src/Skoruba.IdentityServer4.Admin/Styles/controls/picker.scss index e10cbd724..9c1bd5dc7 100644 --- a/src/Skoruba.IdentityServer4.Admin/Styles/controls/picker.scss +++ b/src/Skoruba.IdentityServer4.Admin/Styles/controls/picker.scss @@ -1,45 +1,60 @@ .picker { - hr { - margin-bottom: 5px; - } - - .button { - &__add { - margin-top: 5px; - margin-bottom: 5px; - margin-right: 5px; - white-space: normal; - text-transform: none; - } - - &__delete { - margin-top: 5px; - margin-bottom: 5px; - margin-right: 5px; - white-space: normal; - text-transform: none; - } - - &__show-all { - margin-top: 5px; - margin-bottom: 5px; - margin-right: 5px; - white-space: normal; - text-transform: none; - } - } - - .block { - &__buttons { - &__add { - margin-top: 4px; - } - } - } - - .search-title { - color: gray; - font-size: 12px; - } + hr { + margin-bottom: 5px; + } + + .button { + + &__text { + display: inline-block; + text-align: center; + vertical-align: middle; + margin: 3px; + border: 1px solid transparent; + padding: .375rem .75rem; + font-size: 1rem; + line-height: 1.5; + border-radius: .25rem; + border-color: #007bff; + color: #007bff; + } + + &__add { + margin-top: 5px; + margin-bottom: 5px; + margin-right: 5px; + white-space: normal; + text-transform: none; + } + + &__delete, &__update { + margin-top: 5px; + margin-bottom: 5px; + margin-right: 5px; + white-space: normal; + text-transform: none; + } + + &__show-all { + margin-top: 5px; + margin-bottom: 5px; + margin-right: 5px; + white-space: normal; + text-transform: none; + } + } + + .block { + &__buttons { + &__add { + margin-top: 4px; + } + } + } + + .search-title { + color: gray; + font-size: 12px; + } } diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/Role.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/Role.cshtml index d2afd2694..2cd094b33 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/Role.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/Role.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model RoleDto +@model RoleDto @inject IViewLocalizer Localizer @{ @@ -28,7 +28,7 @@
- @if (!EqualityComparer.Default.Equals(Model.Id, default(int))) + @if (!EqualityComparer.Default.Equals(Model.Id, default(string))) {
diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleClaims.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleClaims.cshtml index e030beaa8..d6424afa0 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleClaims.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleClaims.cshtml @@ -1,7 +1,7 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity @using Skoruba.IdentityServer4.Admin.BusinessLogic.Shared.Dtos.Common -@model RoleClaimsDto +@model RoleClaimsDto @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleClaimsDelete.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleClaimsDelete.cshtml index 5f06ca8a6..7e14b2753 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleClaimsDelete.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleClaimsDelete.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model RoleClaimsDto +@model RoleClaimsDto @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleDelete.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleDelete.cshtml index bbabb7c6b..317e583ae 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleDelete.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/RoleDelete.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model RoleDto +@model RoleDto @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/Roles.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/Roles.cshtml index 6f90ec5b5..ea4182030 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/Roles.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/Roles.cshtml @@ -1,7 +1,7 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity @using Skoruba.IdentityServer4.Admin.BusinessLogic.Shared.Dtos.Common -@model RolesDto, int> +@model RolesDto, string> @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserChangePassword.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserChangePassword.cshtml index af53e0023..94545e80d 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserChangePassword.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserChangePassword.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model UserChangePasswordDto +@model UserChangePasswordDto @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserClaims.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserClaims.cshtml index bc32191f3..b8bd9691f 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserClaims.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserClaims.cshtml @@ -1,7 +1,7 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity @using Skoruba.IdentityServer4.Admin.BusinessLogic.Shared.Dtos.Common -@model UserClaimsDto +@model UserClaimsDto @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserClaimsDelete.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserClaimsDelete.cshtml index b722c8438..5eda0786f 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserClaimsDelete.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserClaimsDelete.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model UserClaimsDto +@model UserClaimsDto @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserDelete.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserDelete.cshtml index 5a3742037..f7e0f8d82 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserDelete.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserDelete.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model UserDto +@model UserDto @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProfile.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProfile.cshtml index 91e6a22ed..4e4ba1ee7 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProfile.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProfile.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model UserDto +@model UserDto @inject IViewLocalizer Localizer @{ @@ -27,7 +27,7 @@
- @if (!EqualityComparer.Default.Equals(Model.Id, default(int))) + @if (!EqualityComparer.Default.Equals(Model.Id, default(string))) {
diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProviders.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProviders.cshtml index b3782e5d6..421ceabe2 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProviders.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProviders.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model UserProvidersDto +@model UserProvidersDto @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProvidersDelete.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProvidersDelete.cshtml index c5f73b604..d793dc34c 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProvidersDelete.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserProvidersDelete.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model UserProviderDto +@model UserProviderDto @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserRoles.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserRoles.cshtml index d43794386..e70150284 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserRoles.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserRoles.cshtml @@ -1,7 +1,7 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity @using Skoruba.IdentityServer4.Admin.BusinessLogic.Shared.Dtos.Common -@model UserRolesDto, int, int> +@model UserRolesDto, string, string> @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserRolesDelete.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserRolesDelete.cshtml index 4b03ae5a2..2d3dc8bfd 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserRolesDelete.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/UserRolesDelete.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity -@model UserRolesDto, int, int> +@model UserRolesDto, string, string> @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/Views/Identity/Users.cshtml b/src/Skoruba.IdentityServer4.Admin/Views/Identity/Users.cshtml index e6306ca4d..35d897db6 100644 --- a/src/Skoruba.IdentityServer4.Admin/Views/Identity/Users.cshtml +++ b/src/Skoruba.IdentityServer4.Admin/Views/Identity/Users.cshtml @@ -1,7 +1,7 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Skoruba.IdentityServer4.Admin.BusinessLogic.Identity.Dtos.Identity @using Skoruba.IdentityServer4.Admin.BusinessLogic.Shared.Dtos.Common -@model UsersDto, int> +@model UsersDto, string> @inject IViewLocalizer Localizer @{ diff --git a/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/css/web.css b/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/css/web.css index 0f6c60592..836e87141 100644 --- a/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/css/web.css +++ b/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/css/web.css @@ -417,6 +417,19 @@ input:checked + .slider:before { .picker hr { margin-bottom: 5px; } +.picker .button__text { + display: inline-block; + text-align: center; + vertical-align: middle; + margin: 3px; + border: 1px solid transparent; + padding: .375rem .75rem; + font-size: 1rem; + line-height: 1.5; + border-radius: .25rem; + border-color: #007bff; + color: #007bff; } + .picker .button__add { margin-top: 5px; margin-bottom: 5px; @@ -424,7 +437,7 @@ input:checked + .slider:before { white-space: normal; text-transform: none; } -.picker .button__delete { +.picker .button__delete, .picker .button__update { margin-top: 5px; margin-bottom: 5px; margin-right: 5px; diff --git a/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/css/web.min.css b/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/css/web.min.css index bf267a519..da6f0fef8 100644 --- a/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/css/web.min.css +++ b/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/css/web.min.css @@ -1 +1 @@ -.slider,.slider:before{transition:.4s;position:absolute}.slider,label.radio-img>h3,label>h4{cursor:pointer}@media (max-width:767px){.menu{text-align:center}.menu-logo{font-size:1.2em}.menu-item{display:none}.menu-button{display:block}.welcome-block h1{font-size:2em}}@media (min-width:768px){.menu-item{display:block}.menu-button{display:none}}.gravatar-image{max-width:none}.border-top{border-top:1px solid #e5e5e5}.border-bottom{border-bottom:1px solid #e5e5e5}.box-shadow{box-shadow:0 .25rem .75rem rgba(0,0,0,.05)}.welcome-block{max-width:700px}.logo,.logo:hover{color:#000;text-decoration:none}.col-m-5{margin:5px}.col-m-10{margin:10px}.col-m-15{margin:15px}.col-m-20{margin:20px}.col-m-25{margin:25px}.col-m-30{margin:30px}.col-m-35{margin:35px}.col-m-40{margin:40px}.col-m-45{margin:45px}.col-m-50{margin:50px}.col-m-b-5{margin-bottom:5px}.col-m-b-10{margin-bottom:10px}.col-m-b-15{margin-bottom:15px}.col-m-b-20{margin-bottom:20px}.col-m-b-25{margin-bottom:25px}.col-m-b-30{margin-bottom:30px}.col-m-b-35{margin-bottom:35px}.col-m-b-40{margin-bottom:40px}.col-m-b-45{margin-bottom:45px}.col-m-b-50{margin-bottom:50px}.col-m-t-5{margin-top:5px}.col-m-t-10{margin-top:10px}.col-m-t-15{margin-top:15px}.col-m-t-20{margin-top:20px}.col-m-t-25{margin-top:25px}.col-m-t-30{margin-top:30px}.col-m-t-35{margin-top:35px}.col-m-t-40{margin-top:40px}.col-m-t-45{margin-top:45px}.col-m-t-50{margin-top:50px}.col-m-l-5{margin-left:5px}.col-m-l-10{margin-left:10px}.col-m-l-15{margin-left:15px}.col-m-l-20{margin-left:20px}.col-m-l-25{margin-left:25px}.col-m-l-30{margin-left:30px}.col-m-l-35{margin-left:35px}.col-m-l-40{margin-left:40px}.col-m-l-45{margin-left:45px}.col-m-l-50{margin-left:50px}.col-m-r-5{margin-right:5px}.col-m-r-10{margin-right:10px}.col-m-r-15{margin-right:15px}.col-m-r-20{margin-right:20px}.col-m-r-25{margin-right:25px}.col-m-r-30{margin-right:30px}.col-m-r-35{margin-right:35px}.col-m-r-40{margin-right:40px}.col-m-r-45{margin-right:45px}.col-m-r-50{margin-right:50px}.col-p-5{padding:5px}.col-p-10{padding:10px}.col-p-15{padding:15px}.col-p-20{padding:20px}.col-p-25{padding:25px}.col-p-30{padding:30px}.col-p-35{padding:35px}.col-p-40{padding:40px}.col-p-45{padding:45px}.col-p-50{padding:50px}.col-p-b-5{padding-bottom:5px}.col-p-b-10{padding-bottom:10px}.col-p-b-15{padding-bottom:15px}.col-p-b-20{padding-bottom:20px}.col-p-b-25{padding-bottom:25px}.col-p-b-30{padding-bottom:30px}.col-p-b-35{padding-bottom:35px}.col-p-b-40{padding-bottom:40px}.col-p-b-45{padding-bottom:45px}.col-p-b-50{padding-bottom:50px}.col-p-t-5{padding-top:5px}.col-p-t-10{padding-top:10px}.col-p-t-15{padding-top:15px}.col-p-t-20{padding-top:20px}.col-p-t-25{padding-top:25px}.col-p-t-30{padding-top:30px}.col-p-t-35{padding-top:35px}.col-p-t-40{padding-top:40px}.col-p-t-45{padding-top:45px}.col-p-t-50{padding-top:50px}.col-p-l-5{padding-left:5px}.col-p-l-10{padding-left:10px}.col-p-l-15{padding-left:15px}.col-p-l-20{padding-left:20px}.col-p-l-25{padding-left:25px}.col-p-l-30{padding-left:30px}.col-p-l-35{padding-left:35px}.col-p-l-40{padding-left:40px}.col-p-l-45{padding-left:45px}.col-p-l-50{padding-left:50px}.col-p-r-5{padding-right:5px}.col-p-r-10{padding-right:10px}.col-p-r-15{padding-right:15px}.col-p-r-20{padding-right:20px}.col-p-r-25{padding-right:25px}.col-p-r-30{padding-right:30px}.col-p-r-35{padding-right:35px}.col-p-r-40{padding-right:40px}.col-p-r-45{padding-right:45px}.col-p-r-50{padding-right:50px}.col-form-label{text-align:right;font-weight:700}@media (max-width:575px){.col-form-label{text-align:left}}.navbar-brand>img{max-height:31px}.navbar-brand.logo{padding:10px}.navbar-brand-image-mobile{display:none}@media (max-width:767px){.navbar-brand-image{display:none}.navbar-brand-image-mobile{display:block;padding-left:0;padding-right:0}}.validation-summary-errors{color:#ff3834;border:1px solid #ffd3d3;background:0 0;padding-top:9px;-ms-border-radius:4px;border-radius:4px;margin-bottom:15px;margin-top:20px}.switch{display:inline-block;height:28px;position:relative;width:60px;margin-top:5px}.switch input{display:none}.slider{background-color:#ccc;bottom:0;left:0;right:0;top:0}.slider:before{background-color:#fff;bottom:4px;content:"";height:20px;left:7px;width:20px}input:checked+.slider{background-color:#007bff}input:checked+.slider:before{transform:translateX(26px)}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}.picker hr{margin-bottom:5px}.picker .button__add,.picker .button__delete,.picker .button__show-all{margin-top:5px;margin-bottom:5px;margin-right:5px;white-space:normal;text-transform:none}.picker .block__buttons__add{margin-top:4px}.picker .search-title{color:gray;font-size:12px}#toast-container>div{opacity:1;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);filter:alpha(opacity=100)}label.radio-img>input{visibility:hidden;position:absolute}label.radio-img>input+img{cursor:pointer;border:2px solid transparent}label.radio-img>input:checked+img{background-color:#ffe7ac;-ms-border-radius:15px;border-radius:15px}label.radio-img>input:checked~h3,label.radio-img>input:checked~h4{color:#007bff;text-decoration:underline}.client .action-butons{margin:20px 0} \ No newline at end of file +.slider,.slider:before{transition:.4s;position:absolute}.slider,label.radio-img>h3,label>h4{cursor:pointer}@media (max-width:767px){.menu{text-align:center}.menu-logo{font-size:1.2em}.menu-item{display:none}.menu-button{display:block}.welcome-block h1{font-size:2em}}@media (min-width:768px){.menu-item{display:block}.menu-button{display:none}}.gravatar-image{max-width:none}.border-top{border-top:1px solid #e5e5e5}.border-bottom{border-bottom:1px solid #e5e5e5}.box-shadow{box-shadow:0 .25rem .75rem rgba(0,0,0,.05)}.welcome-block{max-width:700px}.logo,.logo:hover{color:#000;text-decoration:none}.col-m-5{margin:5px}.col-m-10{margin:10px}.col-m-15{margin:15px}.col-m-20{margin:20px}.col-m-25{margin:25px}.col-m-30{margin:30px}.col-m-35{margin:35px}.col-m-40{margin:40px}.col-m-45{margin:45px}.col-m-50{margin:50px}.col-m-b-5{margin-bottom:5px}.col-m-b-10{margin-bottom:10px}.col-m-b-15{margin-bottom:15px}.col-m-b-20{margin-bottom:20px}.col-m-b-25{margin-bottom:25px}.col-m-b-30{margin-bottom:30px}.col-m-b-35{margin-bottom:35px}.col-m-b-40{margin-bottom:40px}.col-m-b-45{margin-bottom:45px}.col-m-b-50{margin-bottom:50px}.col-m-t-5{margin-top:5px}.col-m-t-10{margin-top:10px}.col-m-t-15{margin-top:15px}.col-m-t-20{margin-top:20px}.col-m-t-25{margin-top:25px}.col-m-t-30{margin-top:30px}.col-m-t-35{margin-top:35px}.col-m-t-40{margin-top:40px}.col-m-t-45{margin-top:45px}.col-m-t-50{margin-top:50px}.col-m-l-5{margin-left:5px}.col-m-l-10{margin-left:10px}.col-m-l-15{margin-left:15px}.col-m-l-20{margin-left:20px}.col-m-l-25{margin-left:25px}.col-m-l-30{margin-left:30px}.col-m-l-35{margin-left:35px}.col-m-l-40{margin-left:40px}.col-m-l-45{margin-left:45px}.col-m-l-50{margin-left:50px}.col-m-r-5{margin-right:5px}.col-m-r-10{margin-right:10px}.col-m-r-15{margin-right:15px}.col-m-r-20{margin-right:20px}.col-m-r-25{margin-right:25px}.col-m-r-30{margin-right:30px}.col-m-r-35{margin-right:35px}.col-m-r-40{margin-right:40px}.col-m-r-45{margin-right:45px}.col-m-r-50{margin-right:50px}.col-p-5{padding:5px}.col-p-10{padding:10px}.col-p-15{padding:15px}.col-p-20{padding:20px}.col-p-25{padding:25px}.col-p-30{padding:30px}.col-p-35{padding:35px}.col-p-40{padding:40px}.col-p-45{padding:45px}.col-p-50{padding:50px}.col-p-b-5{padding-bottom:5px}.col-p-b-10{padding-bottom:10px}.col-p-b-15{padding-bottom:15px}.col-p-b-20{padding-bottom:20px}.col-p-b-25{padding-bottom:25px}.col-p-b-30{padding-bottom:30px}.col-p-b-35{padding-bottom:35px}.col-p-b-40{padding-bottom:40px}.col-p-b-45{padding-bottom:45px}.col-p-b-50{padding-bottom:50px}.col-p-t-5{padding-top:5px}.col-p-t-10{padding-top:10px}.col-p-t-15{padding-top:15px}.col-p-t-20{padding-top:20px}.col-p-t-25{padding-top:25px}.col-p-t-30{padding-top:30px}.col-p-t-35{padding-top:35px}.col-p-t-40{padding-top:40px}.col-p-t-45{padding-top:45px}.col-p-t-50{padding-top:50px}.col-p-l-5{padding-left:5px}.col-p-l-10{padding-left:10px}.col-p-l-15{padding-left:15px}.col-p-l-20{padding-left:20px}.col-p-l-25{padding-left:25px}.col-p-l-30{padding-left:30px}.col-p-l-35{padding-left:35px}.col-p-l-40{padding-left:40px}.col-p-l-45{padding-left:45px}.col-p-l-50{padding-left:50px}.col-p-r-5{padding-right:5px}.col-p-r-10{padding-right:10px}.col-p-r-15{padding-right:15px}.col-p-r-20{padding-right:20px}.col-p-r-25{padding-right:25px}.col-p-r-30{padding-right:30px}.col-p-r-35{padding-right:35px}.col-p-r-40{padding-right:40px}.col-p-r-45{padding-right:45px}.col-p-r-50{padding-right:50px}.col-form-label{text-align:right;font-weight:700}@media (max-width:575px){.col-form-label{text-align:left}}.navbar-brand>img{max-height:31px}.navbar-brand.logo{padding:10px}.navbar-brand-image-mobile{display:none}@media (max-width:767px){.navbar-brand-image{display:none}.navbar-brand-image-mobile{display:block;padding-left:0;padding-right:0}}.validation-summary-errors{color:#ff3834;border:1px solid #ffd3d3;background:0 0;padding-top:9px;-ms-border-radius:4px;border-radius:4px;margin-bottom:15px;margin-top:20px}.switch{display:inline-block;height:28px;position:relative;width:60px;margin-top:5px}.switch input{display:none}.slider{background-color:#ccc;bottom:0;left:0;right:0;top:0}.slider:before{background-color:#fff;bottom:4px;content:"";height:20px;left:7px;width:20px}input:checked+.slider{background-color:#007bff}input:checked+.slider:before{transform:translateX(26px)}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}.picker hr{margin-bottom:5px}.picker .button__text{display:inline-block;text-align:center;vertical-align:middle;margin:3px;border:1px solid #007bff;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;color:#007bff}.picker .button__add,.picker .button__delete,.picker .button__show-all,.picker .button__update{margin-top:5px;margin-bottom:5px;margin-right:5px;white-space:normal;text-transform:none}.picker .block__buttons__add{margin-top:4px}.picker .search-title{color:gray;font-size:12px}#toast-container>div{opacity:1;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);filter:alpha(opacity=100)}label.radio-img>input{visibility:hidden;position:absolute}label.radio-img>input+img{cursor:pointer;border:2px solid transparent}label.radio-img>input:checked+img{background-color:#ffe7ac;-ms-border-radius:15px;border-radius:15px}label.radio-img>input:checked~h3,label.radio-img>input:checked~h4{color:#007bff;text-decoration:underline}.client .action-butons{margin:20px 0} \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/js/bundle.min.js b/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/js/bundle.min.js index e8b9bc577..08094eb16 100644 --- a/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/js/bundle.min.js +++ b/src/Skoruba.IdentityServer4.Admin/wwwroot/dist/js/bundle.min.js @@ -1 +1 @@ -if(function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(T,e){"use strict";var t=[],C=T.document,i=Object.getPrototypeOf,s=t.slice,m=t.concat,l=t.push,r=t.indexOf,n={},o=n.toString,g=n.hasOwnProperty,a=g.toString,u=a.call(Object),v={},y=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},b=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,noModule:!0};function _(e,t,n){var i,r=(t=t||C).createElement("script");if(r.text=e,n)for(i in c)n[i]&&(r[i]=n[i]);t.head.appendChild(r).parentNode.removeChild(r)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var d="3.3.1",D=function(e,t){return new D.fn.init(e,t)},h=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function f(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!y(e)&&!b(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+j+")"+j+"*"),W=new RegExp("="+j+"*([^\\]'\"]*?)"+j+"*\\]","g"),U=new RegExp(R),$=new RegExp("^"+H+"$"),z={ID:new RegExp("^#("+H+")"),CLASS:new RegExp("^\\.("+H+")"),TAG:new RegExp("^("+H+"|[*])"),ATTR:new RegExp("^"+F),PSEUDO:new RegExp("^"+R),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+j+"*(even|odd|(([+-]|)(\\d*)n|)"+j+"*(?:([+-]|)"+j+"*(\\d+)|))"+j+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+j+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+j+"*((?:-\\d)?\\d*)"+j+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Z=/[+~]/,X=new RegExp("\\\\([\\da-f]{1,6}"+j+"?|("+j+")|.)","ig"),ee=function(e,t,n){var i="0x"+t-65536;return i!=i||n?t:i<0?String.fromCharCode(i+65536):String.fromCharCode(i>>10|55296,1023&i|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},ie=function(){x()},re=ye(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{O.apply(t=I.call(y.childNodes),y.childNodes),t[y.childNodes.length].nodeType}catch(e){O={apply:t.length?function(e,t){N.apply(e,I.call(t))}:function(e,t){for(var n=e.length,i=0;e[n++]=t[i++];);e.length=n-1}}}function oe(e,t,n,i){var r,o,a,s,l,u,c,d=t&&t.ownerDocument,h=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==h&&9!==h&&11!==h)return n;if(!i&&((t?t.ownerDocument||t:y)!==T&&x(t),t=t||T,C)){if(11!==h&&(l=Q.exec(e)))if(r=l[1]){if(9===h){if(!(a=t.getElementById(r)))return n;if(a.id===r)return n.push(a),n}else if(d&&(a=d.getElementById(r))&&v(t,a)&&a.id===r)return n.push(a),n}else{if(l[2])return O.apply(n,t.getElementsByTagName(e)),n;if((r=l[3])&&f.getElementsByClassName&&t.getElementsByClassName)return O.apply(n,t.getElementsByClassName(r)),n}if(f.qsa&&!E[e+" "]&&(!g||!g.test(e))){if(1!==h)d=t,c=e;else if("object"!==t.nodeName.toLowerCase()){for((s=t.getAttribute("id"))?s=s.replace(te,ne):t.setAttribute("id",s=D),o=(u=p(e)).length;o--;)u[o]="#"+s+" "+ve(u[o]);c=u.join(","),d=Z.test(e)&&me(t.parentNode)||t}if(c)try{return O.apply(n,d.querySelectorAll(c)),n}catch(e){}finally{s===D&&t.removeAttribute("id")}}}return m(e.replace(q,"$1"),t,n,i)}function ae(){var i=[];return function e(t,n){return i.push(t+" ")>_.cacheLength&&delete e[i.shift()],e[t+" "]=n}}function se(e){return e[D]=!0,e}function le(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ue(e,t){for(var n=e.split("|"),i=n.length;i--;)_.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,i=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(i)return i;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function fe(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&re(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function pe(a){return se(function(o){return o=+o,se(function(e,t){for(var n,i=a([],e.length,o),r=i.length;r--;)e[n=i[r]]&&(e[n]=!(t[n]=e[n]))})})}function me(e){return e&&void 0!==e.getElementsByTagName&&e}for(e in f=oe.support={},r=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},x=oe.setDocument=function(e){var t,n,i=e?e.ownerDocument||e:y;return i!==T&&9===i.nodeType&&i.documentElement&&(a=(T=i).documentElement,C=!r(T),y!==T&&(n=T.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",ie,!1):n.attachEvent&&n.attachEvent("onunload",ie)),f.attributes=le(function(e){return e.className="i",!e.getAttribute("className")}),f.getElementsByTagName=le(function(e){return e.appendChild(T.createComment("")),!e.getElementsByTagName("*").length}),f.getElementsByClassName=K.test(T.getElementsByClassName),f.getById=le(function(e){return a.appendChild(e).id=D,!T.getElementsByName||!T.getElementsByName(D).length}),f.getById?(_.filter.ID=function(e){var t=e.replace(X,ee);return function(e){return e.getAttribute("id")===t}},_.find.ID=function(e,t){if(void 0!==t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(_.filter.ID=function(e){var n=e.replace(X,ee);return function(e){var t=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},_.find.ID=function(e,t){if(void 0!==t.getElementById&&C){var n,i,r,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(r=t.getElementsByName(e),i=0;o=r[i++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),_.find.TAG=f.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):f.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,i=[],r=0,o=t.getElementsByTagName(e);if("*"!==e)return o;for(;n=o[r++];)1===n.nodeType&&i.push(n);return i},_.find.CLASS=f.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&C)return t.getElementsByClassName(e)},s=[],g=[],(f.qsa=K.test(T.querySelectorAll))&&(le(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&g.push("[*^$]="+j+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||g.push("\\["+j+"*(?:value|"+L+")"),e.querySelectorAll("[id~="+D+"-]").length||g.push("~="),e.querySelectorAll(":checked").length||g.push(":checked"),e.querySelectorAll("a#"+D+"+*").length||g.push(".#.+[+~]")}),le(function(e){e.innerHTML="";var t=T.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&g.push("name"+j+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&g.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(f.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&le(function(e){f.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",R)}),g=g.length&&new RegExp(g.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,i=t&&t.parentNode;return e===i||!(!i||1!==i.nodeType||!(n.contains?n.contains(i):e.compareDocumentPosition&&16&e.compareDocumentPosition(i)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},S=t?function(e,t){if(e===t)return u=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!f.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument===y&&v(y,e)?-1:t===T||t.ownerDocument===y&&v(y,t)?1:l?P(l,e)-P(l,t):0:4&n?-1:1)}:function(e,t){if(e===t)return u=!0,0;var n,i=0,r=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!r||!o)return e===T?-1:t===T?1:r?-1:o?1:l?P(l,e)-P(l,t):0;if(r===o)return ce(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[i]===s[i];)i++;return i?ce(a[i],s[i]):a[i]===y?-1:s[i]===y?1:0}),T},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==T&&x(e),t=t.replace(W,"='$1']"),f.matchesSelector&&C&&!E[t+" "]&&(!s||!s.test(t))&&(!g||!g.test(t)))try{var n=c.call(e,t);if(n||f.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(X,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(X,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return z.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&U.test(n)&&(t=p(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(X,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=h[e+" "];return t||(t=new RegExp("(^|"+j+")"+e+"("+j+"|$)"))&&h(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,i,r){return function(e){var t=oe.attr(e,n);return null==t?"!="===i:!i||(t+="","="===i?t===r:"!="===i?t!==r:"^="===i?r&&0===t.indexOf(r):"*="===i?r&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function A(e,n,i){return y(n)?D.grep(e,function(e,t){return!!n.call(e,t,e)!==i}):n.nodeType?D.grep(e,function(e){return e===n!==i}):"string"!=typeof n?D.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(D.fn.init=function(e,t,n){var i,r;if(!e)return this;if(n=n||N,"string"!=typeof e)return e.nodeType?(this[0]=e,this.length=1,this):y(e)?void 0!==n.ready?n.ready(e):e(D):D.makeArray(e,this);if(!(i="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:O.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof D?t[0]:t,D.merge(this,D.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),M.test(i[1])&&D.isPlainObject(t))for(i in t)y(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(r=C.getElementById(i[2]))&&(this[0]=r,this.length=1),this}).prototype=D.fn,N=D(C);var I=/^(?:parents|prev(?:Until|All))/,P={children:!0,contents:!0,next:!0,prev:!0};function L(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}D.fn.extend({has:function(e){var t=D(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]+)/i,de=/^$|^module$|\/(?:java|ecma)script/i,he={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function fe(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?D.merge([e],n):n}function pe(e,t){for(var n=0,i=e.length;nx",v.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var be=C.documentElement,_e=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,xe=/^([^.]*)(?:\.(.+)|)/;function Te(){return!0}function Ce(){return!1}function De(){try{return C.activeElement}catch(e){}}function ke(e,t,n,i,r,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(i=i||n,n=void 0),t)ke(e,s,n,i,t[s],o);return e}if(null==i&&null==r?(r=n,i=n=void 0):null==r&&("string"==typeof n?(r=i,i=void 0):(r=i,i=n,n=void 0)),!1===r)r=Ce;else if(!r)return e;return 1===o&&(a=r,(r=function(e){return D().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=D.guid++)),e.each(function(){D.event.add(this,t,r,i,n)})}D.event={global:{},add:function(t,e,n,i,r){var o,a,s,l,u,c,d,h,f,p,m,g=K.get(t);if(g)for(n.handler&&(n=(o=n).handler,r=o.selector),r&&D.find.matchesSelector(be,r),n.guid||(n.guid=D.guid++),(l=g.events)||(l=g.events={}),(a=g.handle)||(a=g.handle=function(e){return void 0!==D&&D.event.triggered!==e.type?D.event.dispatch.apply(t,arguments):void 0}),u=(e=(e||"").match(j)||[""]).length;u--;)f=m=(s=xe.exec(e[u])||[])[1],p=(s[2]||"").split(".").sort(),f&&(d=D.event.special[f]||{},f=(r?d.delegateType:d.bindType)||f,d=D.event.special[f]||{},c=D.extend({type:f,origType:m,data:i,handler:n,guid:n.guid,selector:r,needsContext:r&&D.expr.match.needsContext.test(r),namespace:p.join(".")},o),(h=l[f])||((h=l[f]=[]).delegateCount=0,d.setup&&!1!==d.setup.call(t,i,p,a)||t.addEventListener&&t.addEventListener(f,a)),d.add&&(d.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),r?h.splice(h.delegateCount++,0,c):h.push(c),D.event.global[f]=!0)},remove:function(e,t,n,i,r){var o,a,s,l,u,c,d,h,f,p,m,g=K.hasData(e)&&K.get(e);if(g&&(l=g.events)){for(u=(t=(t||"").match(j)||[""]).length;u--;)if(f=m=(s=xe.exec(t[u])||[])[1],p=(s[2]||"").split(".").sort(),f){for(d=D.event.special[f]||{},h=l[f=(i?d.delegateType:d.bindType)||f]||[],s=s[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=h.length;o--;)c=h[o],!r&&m!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||i&&i!==c.selector&&("**"!==i||!c.selector)||(h.splice(o,1),c.selector&&h.delegateCount--,d.remove&&d.remove.call(e,c));a&&!h.length&&(d.teardown&&!1!==d.teardown.call(e,p,g.handle)||D.removeEvent(e,f,g.handle),delete l[f])}else for(f in l)D.event.remove(e,f+t[u],n,i,!0);D.isEmptyObject(l)&&K.remove(e,"handle events")}},dispatch:function(e){var t,n,i,r,o,a,s=D.event.fix(e),l=new Array(arguments.length),u=(K.get(this,"events")||{})[s.type]||[],c=D.event.special[s.type]||{};for(l[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,Se=/\s*$/g;function Ne(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&D(e).children("tbody")[0]||e}function Oe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Ie(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,i,r,o,a,s,l,u;if(1===t.nodeType){if(K.hasData(e)&&(o=K.access(e),a=K.set(t,o),u=o.events))for(r in delete a.handle,a.events={},u)for(n=0,i=u[r].length;n")},clone:function(e,t,n){var i,r,o,a,s,l,u,c=e.cloneNode(!0),d=D.contains(e.ownerDocument,e);if(!(v.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||D.isXMLDoc(e)))for(a=fe(c),i=0,r=(o=fe(e)).length;i").prop({charset:n.scriptCharset,src:n.url}).on("load error",r=function(e){i.remove(),r=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(i[0])},abort:function(){r&&r()}}});var Vt,Wt=[],Ut=/(=)\?(?=&|$)|\?\?/;D.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Wt.pop()||D.expando+"_"+wt++;return this[e]=!0,e}}),D.ajaxPrefilter("json jsonp",function(e,t,n){var i,r,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return i=e.jsonpCallback=y(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+i):!1!==e.jsonp&&(e.url+=(xt.test(e.url)?"&":"?")+e.jsonp+"="+i),e.converters["script json"]=function(){return o||D.error(i+" was not called"),o[0]},e.dataTypes[0]="json",r=T[i],T[i]=function(){o=arguments},n.always(function(){void 0===r?D(T).removeProp(i):T[i]=r,e[i]&&(e.jsonpCallback=t.jsonpCallback,Wt.push(i)),o&&y(r)&&r(o[0]),o=r=void 0}),"script"}),v.createHTMLDocument=((Vt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),D.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((i=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(i)):t=C),o=!n&&[],(r=M.exec(e))?[t.createElement(r[1])]:(r=ye([e],t,o),o&&o.length&&D(o).remove(),D.merge([],r.childNodes)));var i,r,o},D.fn.load=function(e,t,n){var i,r,o,a=this,s=e.indexOf(" ");return-1").append(D.parseHTML(e)).find(i):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},D.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){D.fn[t]=function(e){return this.on(t,e)}}),D.expr.pseudos.animated=function(t){return D.grep(D.timers,function(e){return t===e.elem}).length},D.offset={setOffset:function(e,t,n){var i,r,o,a,s,l,u=D.css(e,"position"),c=D(e),d={};"static"===u&&(e.style.position="relative"),s=c.offset(),o=D.css(e,"top"),l=D.css(e,"left"),r=("absolute"===u||"fixed"===u)&&-1<(o+l).indexOf("auto")?(a=(i=c.position()).top,i.left):(a=parseFloat(o)||0,parseFloat(l)||0),y(t)&&(t=t.call(e,n,D.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+r),"using"in t?t.using.call(e,d):c.css(d)}},D.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){D.offset.setOffset(this,t,e)});var e,n,i=this[0];return i?i.getClientRects().length?(e=i.getBoundingClientRect(),n=i.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,i=this[0],r={top:0,left:0};if("fixed"===D.css(i,"position"))t=i.getBoundingClientRect();else{for(t=this.offset(),n=i.ownerDocument,e=i.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===D.css(e,"position");)e=e.parentNode;e&&e!==i&&1===e.nodeType&&((r=D(e).offset()).top+=D.css(e,"borderTopWidth",!0),r.left+=D.css(e,"borderLeftWidth",!0))}return{top:t.top-r.top-D.css(i,"marginTop",!0),left:t.left-r.left-D.css(i,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===D.css(e,"position");)e=e.offsetParent;return e||be})}}),D.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,r){var o="pageYOffset"===r;D.fn[t]=function(e){return V(this,function(e,t,n){var i;if(b(e)?i=e:9===e.nodeType&&(i=e.defaultView),void 0===n)return i?i[r]:e[t];i?i.scrollTo(o?i.pageXOffset:n,o?n:i.pageYOffset):e[t]=n},t,e,arguments.length)}}),D.each(["top","left"],function(e,n){D.cssHooks[n]=qe(v.pixelPosition,function(e,t){if(t)return t=Ye(e,n),He.test(t)?D(e).position()[n]+"px":t})}),D.each({Height:"height",Width:"width"},function(a,s){D.each({padding:"inner"+a,content:s,"":"outer"+a},function(i,o){D.fn[o]=function(e,t){var n=arguments.length&&(i||"boolean"!=typeof e),r=i||(!0===e||!0===t?"margin":"border");return V(this,function(e,t,n){var i;return b(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(i=e.documentElement,Math.max(e.body["scroll"+a],i["scroll"+a],e.body["offset"+a],i["offset"+a],i["client"+a])):void 0===n?D.css(e,t,r):D.style(e,t,n,r)},s,n?e:void 0,n)}})}),D.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){D.fn[n]=function(e,t){return 0").attr("name",i.submitButton.name).val(c(i.submitButton).val()).appendTo(i.currentForm)),!(i.settings.submitHandler&&!i.settings.debug)||(t=i.settings.submitHandler.call(i,i.currentForm,n),e&&e.remove(),void 0!==t&&t)}return i.settings.debug&&n.preventDefault(),i.cancelSubmit?(i.cancelSubmit=!1,e()):i.form()?i.pendingRequest?!(i.formSubmitted=!0):e():(i.focusInvalid(),!1)})),i)}e&&e.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing.")},valid:function(){var e,t,n;return c(this[0]).is("form")?e=this.validate().form():(n=[],e=!0,t=c(this[0].form).validate(),this.each(function(){(e=t.element(this)&&e)||(n=n.concat(t.errorList))}),t.errorList=n),e},rules:function(e,t){var n,i,r,o,a,s,l=this[0],u=void 0!==this.attr("contenteditable")&&"false"!==this.attr("contenteditable");if(null!=l&&(!l.form&&u&&(l.form=this.closest("form")[0],l.name=this.attr("name")),null!=l.form)){if(e)switch(i=(n=c.data(l.form,"validator").settings).rules,r=c.validator.staticRules(l),e){case"add":c.extend(r,c.validator.normalizeRule(t)),delete r.messages,i[l.name]=r,t.messages&&(n.messages[l.name]=c.extend(n.messages[l.name],t.messages));break;case"remove":return t?(s={},c.each(t.split(/\s/),function(e,t){s[t]=r[t],delete r[t]}),s):(delete i[l.name],r)}return(o=c.validator.normalizeRules(c.extend({},c.validator.classRules(l),c.validator.attributeRules(l),c.validator.dataRules(l),c.validator.staticRules(l)),l)).required&&(a=o.required,delete o.required,o=c.extend({required:a},o)),o.remote&&(a=o.remote,delete o.remote,o=c.extend(o,{remote:a})),o}}}),c.extend(c.expr.pseudos||c.expr[":"],{blank:function(e){return!c.trim(""+c(e).val())},filled:function(e){var t=c(e).val();return null!==t&&!!c.trim(""+t)},unchecked:function(e){return!c(e).prop("checked")}}),c.validator=function(e,t){this.settings=c.extend(!0,{},c.validator.defaults,e),this.currentForm=t,this.init()},c.validator.format=function(n,e){return 1===arguments.length?function(){var e=c.makeArray(arguments);return e.unshift(n),c.validator.format.apply(this,e)}:(void 0===e||(2Warning: No message defined for "+e.name+""),i=/\$?\{(\d+)\}/g;return"function"==typeof n?n=n.call(this,t.parameters,e):i.test(n)&&(n=c.validator.format(n.replace(i,"{$1}"),t.parameters)),n},formatAndAdd:function(e,t){var n=this.defaultMessage(e,t);this.errorList.push({message:n,element:e,method:t.method}),this.errorMap[e.name]=n,this.submitted[e.name]=n},addWrapper:function(e){return this.settings.wrapper&&(e=e.add(e.parent(this.settings.wrapper))),e},defaultShowErrors:function(){var e,t,n;for(e=0;this.errorList[e];e++)n=this.errorList[e],this.settings.highlight&&this.settings.highlight.call(this,n.element,this.settings.errorClass,this.settings.validClass),this.showLabel(n.element,n.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(e=0;this.successList[e];e++)this.showLabel(this.successList[e]);if(this.settings.unhighlight)for(e=0,t=this.validElements();t[e];e++)this.settings.unhighlight.call(this,t[e],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return c(this.errorList).map(function(){return this.element})},showLabel:function(e,t){var n,i,r,o,a=this.errorsFor(e),s=this.idOrName(e),l=c(e).attr("aria-describedby");a.length?(a.removeClass(this.settings.validClass).addClass(this.settings.errorClass),a.html(t)):(n=a=c("<"+this.settings.errorElement+">").attr("id",s+"-error").addClass(this.settings.errorClass).html(t||""),this.settings.wrapper&&(n=a.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(n):this.settings.errorPlacement?this.settings.errorPlacement.call(this,n,c(e)):n.insertAfter(e),a.is("label")?a.attr("for",s):0===a.parents("label[for='"+this.escapeCssMeta(s)+"']").length&&(r=a.attr("id"),l?l.match(new RegExp("\\b"+this.escapeCssMeta(r)+"\\b"))||(l+=" "+r):l=r,c(e).attr("aria-describedby",l),(i=this.groups[e.name])&&(o=this,c.each(o.groups,function(e,t){t===i&&c("[name='"+o.escapeCssMeta(e)+"']",o.currentForm).attr("aria-describedby",a.attr("id"))})))),!t&&this.settings.success&&(a.text(""),"string"==typeof this.settings.success?a.addClass(this.settings.success):this.settings.success(a,e)),this.toShow=this.toShow.add(a)},errorsFor:function(e){var t=this.escapeCssMeta(this.idOrName(e)),n=c(e).attr("aria-describedby"),i="label[for='"+t+"'], label[for='"+t+"'] *";return n&&(i=i+", #"+this.escapeCssMeta(n).replace(/\s+/g,", #")),this.errors().filter(i)},escapeCssMeta:function(e){return e.replace(/([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(e){return this.groups[e.name]||(this.checkable(e)?e.name:e.id||e.name)},validationTargetFor:function(e){return this.checkable(e)&&(e=this.findByName(e.name)),c(e).not(this.settings.ignore)[0]},checkable:function(e){return/radio|checkbox/i.test(e.type)},findByName:function(e){return c(this.currentForm).find("[name='"+this.escapeCssMeta(e)+"']")},getLength:function(e,t){switch(t.nodeName.toLowerCase()){case"select":return c("option:selected",t).length;case"input":if(this.checkable(t))return this.findByName(t.name).filter(":checked").length}return e.length},depend:function(e,t){return!this.dependTypes[typeof e]||this.dependTypes[typeof e](e,t)},dependTypes:{boolean:function(e){return e},string:function(e,t){return!!c(e,t.form).length},function:function(e,t){return e(t)}},optional:function(e){var t=this.elementValue(e);return!c.validator.methods.required.call(this,t,e)&&"dependency-mismatch"},startRequest:function(e){this.pending[e.name]||(this.pendingRequest++,c(e).addClass(this.settings.pendingClass),this.pending[e.name]=!0)},stopRequest:function(e,t){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[e.name],c(e).removeClass(this.settings.pendingClass),t&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(c(this.currentForm).submit(),this.submitButton&&c("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!t&&0===this.pendingRequest&&this.formSubmitted&&(c(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(e,t){return t="string"==typeof t&&t||"remote",c.data(e,"previousValue")||c.data(e,"previousValue",{old:null,valid:!0,message:this.defaultMessage(e,{method:t})})},destroy:function(){this.resetForm(),c(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur").find(".validate-lessThan-blur").off(".validate-lessThan").removeClass("validate-lessThan-blur").find(".validate-lessThanEqual-blur").off(".validate-lessThanEqual").removeClass("validate-lessThanEqual-blur").find(".validate-greaterThanEqual-blur").off(".validate-greaterThanEqual").removeClass("validate-greaterThanEqual-blur").find(".validate-greaterThan-blur").off(".validate-greaterThan").removeClass("validate-greaterThan-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(e,t){e.constructor===String?this.classRuleSettings[e]=t:c.extend(this.classRuleSettings,e)},classRules:function(e){var t={},n=c(e).attr("class");return n&&c.each(n.split(" "),function(){this in c.validator.classRuleSettings&&c.extend(t,c.validator.classRuleSettings[this])}),t},normalizeAttributeRule:function(e,t,n,i){/min|max|step/.test(n)&&(null===t||/number|range|text/.test(t))&&(i=Number(i),isNaN(i)&&(i=void 0)),i||0===i?e[n]=i:t===n&&"range"!==t&&(e[n]=!0)},attributeRules:function(e){var t,n,i={},r=c(e),o=e.getAttribute("type");for(t in c.validator.methods)n="required"===t?(""===(n=e.getAttribute(t))&&(n=!0),!!n):r.attr(t),this.normalizeAttributeRule(i,o,t,n);return i.maxlength&&/-1|2147483647|524288/.test(i.maxlength)&&delete i.maxlength,i},dataRules:function(e){var t,n,i={},r=c(e),o=e.getAttribute("type");for(t in c.validator.methods)""===(n=r.data("rule"+t.charAt(0).toUpperCase()+t.substring(1).toLowerCase()))&&(n=!0),this.normalizeAttributeRule(i,o,t,n);return i},staticRules:function(e){var t={},n=c.data(e.form,"validator");return n.settings.rules&&(t=c.validator.normalizeRule(n.settings.rules[e.name])||{}),t},normalizeRules:function(i,r){return c.each(i,function(e,t){if(!1!==t){if(t.param||t.depends){var n=!0;switch(typeof t.depends){case"string":n=!!c(t.depends,r.form).length;break;case"function":n=t.depends.call(r,r)}n?i[e]=void 0===t.param||t.param:(c.data(r.form,"validator").resetElements(c(r)),delete i[e])}}else delete i[e]}),c.each(i,function(e,t){i[e]=c.isFunction(t)&&"normalizer"!==e?t(r):t}),c.each(["minlength","maxlength"],function(){i[this]&&(i[this]=Number(i[this]))}),c.each(["rangelength","range"],function(){var e;i[this]&&(c.isArray(i[this])?i[this]=[Number(i[this][0]),Number(i[this][1])]:"string"==typeof i[this]&&(e=i[this].replace(/[\[\]]/g,"").split(/[\s,]+/),i[this]=[Number(e[0]),Number(e[1])]))}),c.validator.autoCreateRanges&&(null!=i.min&&null!=i.max&&(i.range=[i.min,i.max],delete i.min,delete i.max),null!=i.minlength&&null!=i.maxlength&&(i.rangelength=[i.minlength,i.maxlength],delete i.minlength,delete i.maxlength)),i},normalizeRule:function(e){if("string"==typeof e){var t={};c.each(e.split(/\s/),function(){t[this]=!0}),e=t}return e},addMethod:function(e,t,n){c.validator.methods[e]=t,c.validator.messages[e]=void 0!==n?n:c.validator.messages[e],t.length<3&&c.validator.addClassRules(e,c.validator.normalizeRule(e))},methods:{required:function(e,t,n){if(!this.depend(n,t))return"dependency-mismatch";if("select"!==t.nodeName.toLowerCase())return this.checkable(t)?0=n[0]&&i<=n[1]},min:function(e,t,n){return this.optional(t)||n<=e},max:function(e,t,n){return this.optional(t)||e<=n},range:function(e,t,n){return this.optional(t)||e>=n[0]&&e<=n[1]},step:function(e,t,n){var i,r=c(t).attr("type"),o="Step attribute on input type "+r+" is not supported.",a=new RegExp("\\b"+r+"\\b"),s=function(e){var t=(""+e).match(/(?:\.(\d+))?$/);return t&&t[1]?t[1].length:0},l=function(e){return Math.round(e*Math.pow(10,i))},u=!0;if(r&&!a.test(["text","number","range"].join()))throw new Error(o);return i=s(n),(s(e)>i||l(e)%l(n)!=0)&&(u=!1),this.optional(t)||u},equalTo:function(e,t,n){var i=c(n);return this.settings.onfocusout&&i.not(".validate-equalTo-blur").length&&i.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){c(t).valid()}),e===i.val()},remote:function(o,a,e,s){if(this.optional(a))return"dependency-mismatch";s="string"==typeof s&&s||"remote";var l,t,n,u=this.previousValue(a,s);return this.settings.messages[a.name]||(this.settings.messages[a.name]={}),u.originalMessage=u.originalMessage||this.settings.messages[a.name][s],this.settings.messages[a.name][s]=u.message,e="string"==typeof e&&{url:e}||e,n=c.param(c.extend({data:o},e.data)),u.old===n?u.valid:(u.old=n,(l=this).startRequest(a),(t={})[a.name]=o,c.ajax(c.extend(!0,{mode:"abort",port:"validate"+a.name,dataType:"json",data:t,context:l.currentForm,success:function(e){var t,n,i,r=!0===e||"true"===e;l.settings.messages[a.name][s]=u.originalMessage,r?(i=l.formSubmitted,l.resetInternals(),l.toHide=l.errorsFor(a),l.formSubmitted=i,l.successList.push(a),l.invalid[a.name]=!1,l.showErrors()):(t={},n=e||l.defaultMessage(a,{method:s,parameters:o}),t[a.name]=u.message=n,l.invalid[a.name]=!0,l.showErrors(t)),u.valid=r,l.stopRequest(a,r)}},e)),"pending")}}});var i,r={};return c.ajaxPrefilter?c.ajaxPrefilter(function(e,t,n){var i=e.port;"abort"===e.mode&&(r[i]&&r[i].abort(),r[i]=n)}):(i=c.ajax,c.ajax=function(e){var t=("mode"in e?e:c.ajaxSettings).mode,n=("port"in e?e:c.ajaxSettings).port;return"abort"===t?(r[n]&&r[n].abort(),r[n]=i.apply(this,arguments),r[n]):i.apply(this,arguments)}),c}),function(e){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery-validation"],e):"object"==typeof module&&module.exports?module.exports=e(require("jquery-validation")):jQuery.validator.unobtrusive=e(jQuery)}(function(l){var e,a=l.validator,s="unobtrusiveValidation";function u(e,t,n){e.rules[t]=n,e.message&&(e.messages[t]=e.message)}function c(e){return e.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function d(e){return e.substr(0,e.lastIndexOf(".")+1)}function h(e,t){return 0===e.indexOf("*.")&&(e=e.replace("*.",t)),e}function f(e){var t=l(this),n="__jquery_unobtrusive_validation_form_reset";if(!t.data(n)){t.data(n,!0);try{t.data("validator").resetForm()}finally{t.removeData(n)}t.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),t.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function p(i){var e=l(i),t=e.data(s),n=l.proxy(f,i),r=a.unobtrusive.options||{},o=function(e,t){var n=r[e];n&&l.isFunction(n)&&n.apply(i,t)};return t||(t={options:{errorClass:r.errorClass||"input-validation-error",errorElement:r.errorElement||"span",errorPlacement:function(){(function(e,t){var n=l(this).find("[data-valmsg-for='"+c(t[0].name)+"']"),i=n.attr("data-valmsg-replace"),r=i?!1!==l.parseJSON(i):null;n.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",n),r?(n.empty(),e.removeClass("input-validation-error").appendTo(n)):e.hide()}).apply(i,arguments),o("errorPlacement",arguments)},invalidHandler:function(){(function(e,t){var n=l(this).find("[data-valmsg-summary=true]"),i=n.find("ul");i&&i.length&&t.errorList.length&&(i.empty(),n.addClass("validation-summary-errors").removeClass("validation-summary-valid"),l.each(t.errorList,function(){l("
  • ").html(this.message).appendTo(i)}))}).apply(i,arguments),o("invalidHandler",arguments)},messages:{},rules:{},success:function(){(function(e){var t=e.data("unobtrusiveContainer");if(t){var n=t.attr("data-valmsg-replace"),i=n?l.parseJSON(n):null;t.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),i&&t.empty()}}).apply(i,arguments),o("success",arguments)}},attachValidation:function(){e.off("reset."+s,n).on("reset."+s,n).validate(this.options)},validate:function(){return e.validate(),e.valid()}},e.data(s,t)),t}return a.unobtrusive={adapters:[],parseElement:function(i,e){var t,r,o,a=l(i),s=a.parents("form")[0];s&&((t=p(s)).options.rules[i.name]=r={},t.options.messages[i.name]=o={},l.each(this.adapters,function(){var e="data-val-"+this.name,t=a.attr(e),n={};void 0!==t&&(e+="-",l.each(this.params,function(){n[this]=a.attr(e+this)}),this.adapt({element:i,form:s,message:t,params:n,rules:r,messages:o}))}),l.extend(r,{__dummy__:!0}),e||t.attachValidation())},parse:function(e){var t=l(e),n=t.parents().addBack().filter("form").add(t.find("form")).has("[data-val=true]");t.find("[data-val=true]").each(function(){a.unobtrusive.parseElement(this,!0)}),n.each(function(){var e=p(this);e&&e.attachValidation()})}},(e=a.unobtrusive.adapters).add=function(e,t,n){return n||(n=t,t=[]),this.push({name:e,params:t,adapt:n}),this},e.addBool=function(t,n){return this.add(t,function(e){u(e,n||t,!0)})},e.addMinMax=function(e,i,r,o,t,n){return this.add(e,[t||"min",n||"max"],function(e){var t=e.params.min,n=e.params.max;t&&n?u(e,o,[t,n]):t?u(e,i,t):n&&u(e,r,n)})},e.addSingleVal=function(t,n,i){return this.add(t,[n||"val"],function(e){u(e,i||t,e.params[n])})},a.addMethod("__dummy__",function(e,t,n){return!0}),a.addMethod("regex",function(e,t,n){var i;return!!this.optional(t)||(i=new RegExp(n).exec(e))&&0===i.index&&i[0].length===e.length}),a.addMethod("nonalphamin",function(e,t,n){var i;return n&&(i=(i=e.match(/\W/g))&&i.length>=n),i}),a.methods.extension?(e.addSingleVal("accept","mimtype"),e.addSingleVal("extension","extension")):e.addSingleVal("extension","extension","accept"),e.addSingleVal("regex","pattern"),e.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),e.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),e.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),e.add("equalto",["other"],function(e){var t=d(e.element.name),n=h(e.params.other,t);u(e,"equalTo",l(e.form).find(":input").filter("[name='"+c(n)+"']")[0])}),e.add("required",function(e){"INPUT"===e.element.tagName.toUpperCase()&&"CHECKBOX"===e.element.type.toUpperCase()||u(e,"required",!0)}),e.add("remote",["url","type","additionalfields"],function(i){var r={url:i.params.url,type:i.params.type||"GET",data:{}},o=d(i.element.name);l.each((i.params.additionalfields||i.element.name).replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g),function(e,t){var n=h(t,o);r.data[n]=function(){var e=l(i.form).find(":input").filter("[name='"+c(n)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),u(i,"remote",r)}),e.add("password",["min","nonalphamin","regex"],function(e){e.params.min&&u(e,"minlength",e.params.min),e.params.nonalphamin&&u(e,"nonalphamin",e.params.nonalphamin),e.params.regex&&u(e,"regex",e.params.regex)}),e.add("fileextensions",["extensions"],function(e){u(e,"extension",e.params.extensions)}),l(function(){a.unobtrusive.parse(document)}),a.unobtrusive}),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Popper=t()}(this,function(){"use strict";for(var e="undefined"!=typeof window&&"undefined"!=typeof document,t=["Edge","Trident","Firefox"],n=0,i=0;i=i.clientWidth&&n>=i.clientHeight}),c=0l[e]&&!i.escapeWithReference&&(n=Math.min(c[t],l[e]-("right"===e?c.width:c.height))),w({},t,n)}};return u.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";c=x({},c,d[t](e))}),e.offsets.popper=c,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,r=e.placement.split("-")[0],o=Math.floor,a=-1!==["top","bottom"].indexOf(r),s=a?"right":"bottom",l=a?"left":"top",u=a?"width":"height";return n[s]o(i[s])&&(e.offsets.popper[l]=o(i[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!q(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],o=e.offsets,a=o.popper,s=o.reference,l=-1!==["left","right"].indexOf(r),u=l?"height":"width",c=l?"Top":"Left",d=c.toLowerCase(),h=l?"left":"top",f=l?"bottom":"right",p=M(i)[u];s[f]-pa[f]&&(e.offsets.popper[d]+=s[d]+p-a[f]),e.offsets.popper=T(e.offsets.popper);var m=s[d]+s[u]/2-p/2,g=_(e.instance.popper),v=parseFloat(g["margin"+c],10),y=parseFloat(g["border"+c+"Width"],10),b=m-e.offsets.popper[d]-v-y;return b=Math.max(Math.min(a[u]-p,b),0),e.arrowElement=i,e.offsets.arrow=(w(n={},d,Math.round(b)),w(n,h,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,m){if(P(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var g=h(p.instance.popper,p.instance.reference,m.padding,m.boundariesElement,p.positionFixed),v=p.placement.split("-")[0],y=A(v),b=p.placement.split("-")[1]||"",_=[];switch(m.behavior){case U:_=[v,y];break;case $:_=W(v);break;case z:_=W(v,!0);break;default:_=m.behavior}return _.forEach(function(e,t){if(v!==e||_.length===t+1)return p;v=p.placement.split("-")[0],y=A(v);var n,i=p.offsets.popper,r=p.offsets.reference,o=Math.floor,a="left"===v&&o(i.right)>o(r.left)||"right"===v&&o(i.left)o(r.top)||"bottom"===v&&o(i.top)o(g.right),u=o(i.top)o(g.bottom),d="left"===v&&s||"right"===v&&l||"top"===v&&u||"bottom"===v&&c,h=-1!==["top","bottom"].indexOf(v),f=!!m.flipVariations&&(h&&"start"===b&&s||h&&"end"===b&&l||!h&&"start"===b&&u||!h&&"end"===b&&c);(a||d||f)&&(p.flipped=!0,(a||d)&&(v=_[t+1]),f&&(b="end"===(n=b)?"start":"start"===n?"end":n),p.placement=v+(b?"-"+b:""),p.offsets.popper=x({},p.offsets.popper,N(p.instance.popper,p.offsets.reference,p.placement)),p=I(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,r=i.popper,o=i.reference,a=-1!==["left","right"].indexOf(n),s=-1===["top","left"].indexOf(n);return r[a?"left":"top"]=o[n]-(s?r[a?"width":"height"]:0),e.placement=A(t),e.offsets.popper=T(r),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=O(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightthis._items.length-1||e<0))if(this._isSliding)O(this._element).one(W.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
  • ',trigger:"hover focus",title:"",delay:0,html:!(Dt={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Ct={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},St="out",Mt={HIDE:"hide"+_t,HIDDEN:"hidden"+_t,SHOW:(Et="show")+_t,SHOWN:"shown"+_t,INSERTED:"inserted"+_t,CLICK:"click"+_t,FOCUSIN:"focusin"+_t,FOCUSOUT:"focusout"+_t,MOUSEENTER:"mouseenter"+_t,MOUSELEAVE:"mouseleave"+_t},At="fade",Nt="show",Ot=".tooltip-inner",It=".arrow",Pt="hover",Lt="focus",jt="click",Ht="manual",Ft=function(){function i(e,t){if(void 0===c)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=vt(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),vt(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(vt(this.getTipElement()).hasClass(Nt))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),vt.removeData(this.element,this.constructor.DATA_KEY),vt(this.element).off(this.constructor.EVENT_KEY),vt(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&vt(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===vt(this.element).css("display"))throw new Error("Please use show on visible elements");var e=vt.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){vt(this.element).trigger(e);var n=vt.contains(this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=qn.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&vt(i).addClass(At);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,a=this._getAttachment(o);this.addAttachmentClass(a);var s=!1===this.config.container?document.body:vt(document).find(this.config.container);vt(i).data(this.constructor.DATA_KEY,this),vt.contains(this.element.ownerDocument.documentElement,this.tip)||vt(i).appendTo(s),vt(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new c(this.element,i,{placement:a,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:It},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){t._handlePopperPlacementChange(e)}}),vt(i).addClass(Nt),"ontouchstart"in document.documentElement&&vt(document.body).children().on("mouseover",null,vt.noop);var l=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,vt(t.element).trigger(t.constructor.Event.SHOWN),e===St&&t._leave(null,t)};if(vt(this.tip).hasClass(At)){var u=qn.getTransitionDurationFromElement(this.tip);vt(this.tip).one(qn.TRANSITION_END,l).emulateTransitionEnd(u)}else l()}},e.hide=function(e){var t=this,n=this.getTipElement(),i=vt.Event(this.constructor.Event.HIDE),r=function(){t._hoverState!==Et&&n.parentNode&&n.parentNode.removeChild(n),t._cleanTipClass(),t.element.removeAttribute("aria-describedby"),vt(t.element).trigger(t.constructor.Event.HIDDEN),null!==t._popper&&t._popper.destroy(),e&&e()};if(vt(this.element).trigger(i),!i.isDefaultPrevented()){if(vt(n).removeClass(Nt),"ontouchstart"in document.documentElement&&vt(document.body).children().off("mouseover",null,vt.noop),this._activeTrigger[jt]=!1,this._activeTrigger[Lt]=!1,this._activeTrigger[Pt]=!1,vt(this.tip).hasClass(At)){var o=qn.getTransitionDurationFromElement(n);vt(n).one(qn.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){vt(this.getTipElement()).addClass(xt+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||vt(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(vt(e.querySelectorAll(Ot)),this.getTitle()),vt(e).removeClass(At+" "+Nt)},e.setElementContent=function(e,t){var n=this.config.html;"object"==typeof t&&(t.nodeType||t.jquery)?n?vt(t).parent().is(e)||e.empty().append(t):e.text(vt(t).text()):e[n?"html":"text"](t)},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e||(e="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),e},e._getAttachment=function(e){return Dt[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)vt(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==Ht){var t=e===Pt?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Pt?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;vt(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}vt(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==e)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||vt(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),vt(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Lt:Pt]=!0),vt(t.getTipElement()).hasClass(Nt)||t._hoverState===Et?t._hoverState=Et:(clearTimeout(t._timeout),t._hoverState=Et,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===Et&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||vt(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),vt(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Lt:Pt]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=St,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===St&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){return"number"==typeof(e=l({},this.constructor.Default,vt(this.element).data(),"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),qn.typeCheckConfig(yt,e,this.constructor.DefaultType),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=vt(this.getTipElement()),t=e.attr("class").match(Tt);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(vt(e).removeClass(At),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=vt(this).data(bt),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),vt(this).data(bt,e)),"string"==typeof n)){if(void 0===e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},a(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return kt}},{key:"NAME",get:function(){return yt}},{key:"DATA_KEY",get:function(){return bt}},{key:"Event",get:function(){return Mt}},{key:"EVENT_KEY",get:function(){return _t}},{key:"DefaultType",get:function(){return Ct}}]),i}(),vt.fn[yt]=Ft._jQueryInterface,vt.fn[yt].Constructor=Ft,vt.fn[yt].noConflict=function(){return vt.fn[yt]=wt,Ft._jQueryInterface},Ft),Jn=(Yt="popover",Bt="."+(qt="bs.popover"),Vt=(Rt=t).fn[Yt],Wt="bs-popover",Ut=new RegExp("(^|\\s)"+Wt+"\\S+","g"),$t=l({},Gn.Default,{placement:"right",trigger:"click",content:"",template:''}),zt=l({},Gn.DefaultType,{content:"(string|element|function)"}),Gt="fade",Kt=".popover-header",Qt=".popover-body",Zt={HIDE:"hide"+Bt,HIDDEN:"hidden"+Bt,SHOW:(Jt="show")+Bt,SHOWN:"shown"+Bt,INSERTED:"inserted"+Bt,CLICK:"click"+Bt,FOCUSIN:"focusin"+Bt,FOCUSOUT:"focusout"+Bt,MOUSEENTER:"mouseenter"+Bt,MOUSELEAVE:"mouseleave"+Bt},Xt=function(e){var t,n;function i(){return e.apply(this,arguments)||this}n=e,(t=i).prototype=Object.create(n.prototype),(t.prototype.constructor=t).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(e){Rt(this.getTipElement()).addClass(Wt+"-"+e)},r.getTipElement=function(){return this.tip=this.tip||Rt(this.config.template)[0],this.tip},r.setContent=function(){var e=Rt(this.getTipElement());this.setElementContent(e.find(Kt),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find(Qt),t),e.removeClass(Gt+" "+Jt)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var e=Rt(this.getTipElement()),t=e.attr("class").match(Ut);null!==t&&0=this._offsets[r]&&(void 0===this._offsets[r+1]||e>>0;if("function"!=typeof e)throw TypeError();var i,r=arguments[1];for(i=0;i>16&255)),n.push(String.fromCharCode(i>>8&255)),n.push(String.fromCharCode(255&i)),i=r=0),t+=1;return 12===r?(i>>=4,n.push(String.fromCharCode(255&i))):18===r&&(i>>=2,n.push(String.fromCharCode(i>>8&255)),n.push(String.fromCharCode(255&i))),n.join("")},e.btoa=e.btoa||function(e){e=String(e);var t,n,i,r,o,a,s,l=0,u=[];if(/[^\x00-\xFF]/.test(e))throw Error("InvalidCharacterError");for(;l>2,o=(3&t)<<4|(n=e.charCodeAt(l++))>>4,a=(15&n)<<2|(i=e.charCodeAt(l++))>>6,s=63&i,l===e.length+2?s=a=64:l===e.length+1&&(s=64),u.push(c.charAt(r),c.charAt(o),c.charAt(a),c.charAt(s));return u.join("")},Object.prototype.hasOwnProperty||(Object.prototype.hasOwnProperty=function(e){var t=this.__proto__||this.constructor.prototype;return e in this&&(!(e in t)||t[e]!==this[e])}),function(){if("performance"in r==!1&&(r.performance={}),Date.now=Date.now||function(){return(new Date).getTime()},"now"in r.performance==!1){var e=Date.now();performance.timing&&performance.timing.navigationStart&&(e=performance.timing.navigationStart),r.performance.now=function(){return Date.now()-e}}}(),r.requestAnimationFrame||(r.webkitRequestAnimationFrame&&r.webkitCancelAnimationFrame?((i=r).requestAnimationFrame=function(e){return webkitRequestAnimationFrame(function(){e(i.performance.now())})},i.cancelAnimationFrame=i.webkitCancelAnimationFrame):r.mozRequestAnimationFrame&&r.mozCancelAnimationFrame?((n=r).requestAnimationFrame=function(e){return mozRequestAnimationFrame(function(){e(n.performance.now())})},n.cancelAnimationFrame=n.mozCancelAnimationFrame):((t=r).requestAnimationFrame=function(e){return t.setTimeout(e,1e3/60)},t.cancelAnimationFrame=t.clearTimeout))}}(this),function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Holder=t():e.Holder=t()}(this,function(){return function(n){var i={};function r(e){if(i[e])return i[e].exports;var t=i[e]={exports:{},id:e,loaded:!1};return n[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}return r.m=n,r.c=i,r.p="",r(0)}([function(e,t,n){e.exports=n(1)},function(s,e,E){(function(u){var e=E(2),h=E(3),P=E(6),m=E(7),g=E(8),v=E(9),L=E(10),t=E(11),c=E(12),d=E(15),y=m.extend,b=m.dimensionCheck,_=t.svg_ns,i={version:t.version,addTheme:function(e,t){return null!=e&&null!=t&&(j.settings.themes[e]=t),delete j.vars.cache.themeKeys,this},addImage:function(i,e){return v.getNodeArray(e).forEach(function(e){var t=v.newEl("img"),n={};n[j.setup.dataAttr]=i,v.setAttr(t,n),e.appendChild(t)}),this},setResizeUpdate:function(e,t){e.holderData&&(e.holderData.resizeUpdate=!!t,e.holderData.resizeUpdate&&x(e))},run:function(e){e=e||{};var c={},d=y(j.settings,e);j.vars.preempted=!0,j.vars.dataAttr=d.dataAttr||j.setup.dataAttr,c.renderer=d.renderer?d.renderer:j.setup.renderer,-1===j.setup.renderers.join(",").indexOf(c.renderer)&&(c.renderer=j.setup.supportsSVG?"svg":j.setup.supportsCanvas?"canvas":"html");var t=v.getNodeArray(d.images),n=v.getNodeArray(d.bgnodes),i=v.getNodeArray(d.stylenodes),r=v.getNodeArray(d.objects);return c.stylesheets=[],c.svgXMLStylesheet=!0,c.noFontFallback=!!d.noFontFallback,c.noBackgroundSize=!!d.noBackgroundSize,i.forEach(function(e){if(e.attributes.rel&&e.attributes.href&&"stylesheet"==e.attributes.rel.value){var t=e.attributes.href.value,n=v.newEl("a");n.href=t;var i=n.protocol+"//"+n.host+n.pathname+n.search;c.stylesheets.push(i)}}),n.forEach(function(e){if(u.getComputedStyle){var t=u.getComputedStyle(e,null).getPropertyValue("background-image"),n=e.getAttribute("data-background-src")||t,i=null,r=d.domain+"/",o=n.indexOf(r);if(0===o)i=n;else if(1===o&&"?"===n[0])i=n.slice(1);else{var a=n.substr(o).match(/([^\"]*)"?\)/);if(null!==a)i=a[1];else if(0===n.indexOf("url("))throw"Holder: unable to parse background URL: "+n}if(i){var s=l(i,d);s&&p({mode:"background",el:e,flags:s,engineSettings:c})}}}),r.forEach(function(e){var t={};try{t.data=e.getAttribute("data"),t.dataSrc=e.getAttribute(j.vars.dataAttr)}catch(e){}var n=null!=t.data&&0===t.data.indexOf(d.domain),i=null!=t.dataSrc&&0===t.dataSrc.indexOf(d.domain);n?f(d,c,t.data,e):i&&f(d,c,t.dataSrc,e)}),t.forEach(function(e){var t={};try{t.src=e.getAttribute("src"),t.dataSrc=e.getAttribute(j.vars.dataAttr),t.rendered=e.getAttribute("data-holder-rendered")}catch(e){}var n,i,r,o,a,s=null!=t.src,l=null!=t.dataSrc&&0===t.dataSrc.indexOf(d.domain),u=null!=t.rendered&&"true"==t.rendered;s?0===t.src.indexOf(d.domain)?f(d,c,t.src,e):l&&(u?f(d,c,t.dataSrc,e):(n=t.src,i=d,r=c,o=t.dataSrc,a=e,m.imageExists(n,function(e){e||f(i,r,o,a)}))):l&&f(d,c,t.dataSrc,e)}),this}},j={settings:{domain:"holder.js",images:"img",objects:"object",bgnodes:"body .holderjs",stylenodes:"head link.holderjs",themes:{gray:{bg:"#EEEEEE",fg:"#AAAAAA"},social:{bg:"#3a5a97",fg:"#FFFFFF"},industrial:{bg:"#434A52",fg:"#C2F200"},sky:{bg:"#0D8FDB",fg:"#FFFFFF"},vine:{bg:"#39DBAC",fg:"#1E292C"},lava:{bg:"#F8591A",fg:"#1C2846"}}},defaults:{size:10,units:"pt",scale:1/16}};function f(e,t,n,i){var r=l(n.substr(n.lastIndexOf(e.domain)),e);r&&p({mode:null,el:i,flags:r,engineSettings:t})}function l(e,t){var n={theme:y(j.settings.themes.gray,null),stylesheets:t.stylesheets,instanceOptions:t},i=e.indexOf("?"),r=[e];-1!==i&&(r=[e.slice(0,i),e.slice(i+1)]);var o=r[0].split("/");n.holderURL=e;var a=o[1],s=a.match(/([\d]+p?)x([\d]+p?)/);if(!s)return!1;if(n.fluid=-1!==a.indexOf("p"),n.dimensions={width:s[1].replace("p","%"),height:s[2].replace("p","%")},2===r.length){var l=h.parse(r[1]);if(m.truthy(l.ratio)){n.fluid=!0;var u=parseFloat(n.dimensions.width.replace("%","")),c=parseFloat(n.dimensions.height.replace("%",""));c=Math.floor(c/u*100),u=100,n.dimensions.width=u+"%",n.dimensions.height=c+"%"}if(n.auto=m.truthy(l.auto),l.bg&&(n.theme.bg=m.parseColor(l.bg)),l.fg&&(n.theme.fg=m.parseColor(l.fg)),l.bg&&!l.fg&&(n.autoFg=!0),l.theme&&n.instanceOptions.themes.hasOwnProperty(l.theme)&&(n.theme=y(n.instanceOptions.themes[l.theme],null)),l.text&&(n.text=l.text),l.textmode&&(n.textmode=l.textmode),l.size&&(n.size=l.size),l.font&&(n.font=l.font),l.align&&(n.align=l.align),l.lineWrap&&(n.lineWrap=l.lineWrap),n.nowrap=m.truthy(l.nowrap),n.outline=m.truthy(l.outline),m.truthy(l.random)){j.vars.cache.themeKeys=j.vars.cache.themeKeys||Object.keys(n.instanceOptions.themes);var d=j.vars.cache.themeKeys[0|Math.random()*j.vars.cache.themeKeys.length];n.theme=y(n.instanceOptions.themes[d],null)}}return n}function p(e){var t=e.mode,n=e.el,i=e.flags,r=e.engineSettings,o=i.dimensions,a=i.theme,s=o.width+"x"+o.height;t=null==t?i.fluid?"fluid":"image":t;if(null!=i.text&&(a.text=i.text,"object"===n.nodeName.toLowerCase())){for(var l=a.text.split("\\n"),u=0;u=f||!0===I)&&(k(x,D,S,x.properties.leading),x.add(D),S=0,M+=x.properties.leading,A+=1,(D=new m.Group("line"+A)).y=M),!0!==I&&(C.moveTo(S,0),S+=T.spaceWidth+O.width,D.add(C))}if(k(x,D,S,x.properties.leading),x.add(D),"left"===e.align)x.moveTo(e.width-h,null,null);else if("right"===e.align){for(E in x.children)(D=x.children[E]).moveTo(e.width-D.width,null,null);x.moveTo(0-(e.width-h),null,null)}else{for(E in x.children)(D=x.children[E]).moveTo((x.width-D.width)/2,null,null);x.moveTo((e.width-x.width)/2,null,null)}x.moveTo(null,(e.height-x.height)/2,null),(e.height-x.height)/2<0&&x.moveTo(null,0,null)}else C=new m.Text(e.text),(D=new m.Group("line0")).add(C),x.add(D),"left"===e.align?x.moveTo(e.width-h,null,null):"right"===e.align?x.moveTo(0-(e.width-h),null,null):x.moveTo((e.width-T.boundingBox.width)/2,null,null),x.moveTo(null,(e.height-T.boundingBox.height)/2,null);return p}(a);function l(){var e=null;switch(o.renderer){case"canvas":e=d(s,t);break;case"svg":e=c(s,t);break;default:throw"Holder: invalid renderer: "+o.renderer}return e}if(null==(e=l()))throw"Holder: couldn't render placeholder";"background"==n?(i.style.backgroundImage="url("+e+")",o.noBackgroundSize||(i.style.backgroundSize=a.width+"px "+a.height+"px")):("img"===i.nodeName.toLowerCase()?v.setAttr(i,{src:e}):"object"===i.nodeName.toLowerCase()&&v.setAttr(i,{data:e,type:"image/svg+xml"}),o.reRender&&u.setTimeout(function(){var e=l();if(null==e)throw"Holder: couldn't render placeholder";"img"===i.nodeName.toLowerCase()?v.setAttr(i,{src:e}):"object"===i.nodeName.toLowerCase()&&v.setAttr(i,{data:e,type:"image/svg+xml"})},150)),v.setAttr(i,{"data-holder-rendered":!0})}function x(e){for(var t,n=0,i=(t=null==e||null==e.nodeType?j.vars.resizableImages:[e]).length;n","application/xml")},t.getNodeArray=function(e){var t=null;return"string"==typeof e?t=document.querySelectorAll(e):n.NodeList&&e instanceof n.NodeList?t=e:n.Node&&e instanceof n.Node?t=[e]:n.HTMLCollection&&e instanceof n.HTMLCollection?t=e:e instanceof Array?t=e:null===e&&(t=[]),t=Array.prototype.slice.call(t)}}).call(t,function(){return this}())},function(e,t){var a=function(e,t){"string"==typeof e&&("#"===(this.original=e).charAt(0)&&(e=e.slice(1)),/[^a-f0-9]+/i.test(e)||(3===e.length&&(e=e.replace(/./g,"$&$&")),6===e.length&&(this.alpha=1,t&&t.alpha&&(this.alpha=t.alpha),this.set(parseInt(e,16)))))};a.rgb2hex=function(e,t,n){return[e,t,n].map(function(e){var t=(0|e).toString(16);return e<16&&(t="0"+t),t}).join("")},a.hsl2rgb=function(e,t,n){var i=e/60,r=(1-Math.abs(2*n-1))*t,o=r*(1-Math.abs(parseInt(i)%2-1)),a=n-r/2,s=0,l=0,u=0;return 0<=i&&i<1?(s=r,l=o):1<=i&&i<2?(s=o,l=r):2<=i&&i<3?(l=r,u=o):3<=i&&i<4?(l=o,u=r):4<=i&&i<5?(s=o,u=r):5<=i&&i<6&&(s=r,u=o),s+=a,l+=a,u+=a,[s=parseInt(255*s),l=parseInt(255*l),u=parseInt(255*u)]},a.prototype.set=function(e){this.raw=e;var t=(16711680&this.raw)>>16,n=(65280&this.raw)>>8,i=255&this.raw,r=.2126*t+.7152*n+.0722*i,o=-.09991*t-.33609*n+.436*i,a=.615*t-.55861*n-.05639*i;return this.rgb={r:t,g:n,b:i},this.yuv={y:r,u:o,v:a},this},a.prototype.lighten=function(e){var t=255*(Math.min(1,Math.max(0,Math.abs(e)))*(e<0?-1:1))|0,n=Math.min(255,Math.max(0,this.rgb.r+t)),i=Math.min(255,Math.max(0,this.rgb.g+t)),r=Math.min(255,Math.max(0,this.rgb.b+t)),o=a.rgb2hex(n,i,r);return new a(o)},a.prototype.toHex=function(e){return(e?"#":"")+this.raw.toString(16)},a.prototype.lighterThan=function(e){return e instanceof a||(e=new a(e)),this.yuv.y>e.yuv.y},a.prototype.blendAlpha=function(e){e instanceof a||(e=new a(e));var t=e,n=t.alpha*t.rgb.r+(1-t.alpha)*this.rgb.r,i=t.alpha*t.rgb.g+(1-t.alpha)*this.rgb.g,r=t.alpha*t.rgb.b+(1-t.alpha)*this.rgb.b;return new a(a.rgb2hex(n,i,r))},e.exports=a},function(e,t){e.exports={version:"2.9.4",svg_ns:"http://www.w3.org/2000/svg"}},function(e,t,n){var C=n(13),D=n(8),i=n(11),k=n(7),E=i.svg_ns,S=function(e){var t=e.tag,n=e.content||"";return delete e.tag,delete e.content,[t,n,e]};e.exports=function(e,t){var n,i=t.engineSettings.stylesheets.map(function(e){return''}).join("\n"),r="holder_"+Number(new Date).toString(16),o=e.root,a=o.children.holderTextGroup,s="#"+r+" text { "+(n=a.properties,k.cssProps({fill:n.fill,"font-weight":n.font.weight,"font-family":n.font.family+", monospace","font-size":n.font.size+n.font.units}))+" } ";a.y+=.8*a.textPositionData.boundingBox.height;var l=[];Object.keys(a.children).forEach(function(e){var o=a.children[e];Object.keys(o.children).forEach(function(e){var t=o.children[e],n=a.x+o.x+t.x,i=a.y+o.y+t.y,r=S({tag:"text",content:t.properties.text,x:n,y:i});l.push(r)})});var u,c,d,h,f=S({tag:"g",content:l}),p=null;if(o.children.holderBg.properties.outline){var m=o.children.holderBg.properties.outline;p=S({tag:"path",d:(u=o.children.holderBg.width,c=o.children.holderBg.height,d=m.width,h=d/2,["M",h,h,"H",u-h,"V",c-h,"H",h,"V",0,"M",0,h,"L",u,c-h,"M",0,c-h,"L",u,h].join(" ")),"stroke-width":m.width,stroke:m.fill,fill:"none"})}var g,v=(g=o.children.holderBg,S({tag:"rect",width:g.width,height:g.height,fill:g.properties.fill})),y=[];y.push(v),m&&y.push(p),y.push(f);var b=S({tag:"g",id:r,content:y}),_=S({tag:"style",content:s,type:"text/css"}),w=S({tag:"defs",content:_}),x=S({tag:"svg",content:[w,b],width:o.properties.width,height:o.properties.height,xmlns:E,viewBox:[0,0,o.properties.width,o.properties.height].join(" "),preserveAspectRatio:"none"}),T=C(x);return T=i+T[0],D.svgStringToDataURI(T,"background"===t.mode)}},function(e,t,n){n(14);e.exports=function e(t,n,i){"use strict";var r,o,a,s,l,u,c,d,h,f,p,m,g=1,v=!0;function y(e,t){if(null!==t&&!1!==t&&void 0!==t)return"string"!=typeof t&&"object"!=typeof t?String(t):t}if(i=i||{},"string"==typeof t[0])t[0]=(l=t[0],u=l.match(/^[\w-]+/),c={tag:u?u[0]:"div",attr:{},children:[]},d=l.match(/#([\w-]+)/),h=l.match(/\$([\w-]+)/),f=l.match(/\.[\w-]+/g),d&&(c.attr.id=d[1],i[d[1]]=c),h&&(i[h[1]]=c),f&&(c.attr.class=f.join(" ").replace(/\./g,"")),l.match(/&$/g)&&(v=!1),c);else{if(!Array.isArray(t[0]))throw new Error("First element of array must be a string, or an array and not "+JSON.stringify(t[0]));g=0}for(;g/g,">"))),t[0].children.push(t[g]);else if("number"==typeof t[g])t[0].children.push(t[g]);else if(Array.isArray(t[g])){if(Array.isArray(t[g][0])){if(t[g].reverse().forEach(function(e){t.splice(g+1,0,e)}),0!==g)continue;g++}e(t[g],n,i),t[g][0]&&t[0].children.push(t[g][0])}else if("function"==typeof t[g])a=t[g];else{if("object"!=typeof t[g])throw new TypeError('"'+t[g]+'" is not allowed as a value.');for(o in t[g])t[g].hasOwnProperty(o)&&null!==t[g][o]&&!1!==t[g][o]&&("style"===o&&"object"==typeof t[g][o]?t[0].attr[o]=JSON.stringify(t[g][o],y).slice(2,-2).replace(/","/g,";").replace(/":"/g,":").replace(/\\"/g,"'"):t[0].attr[o]=t[g][o])}}if(!1!==t[0]){for(s in r="<"+t[0].tag,t[0].attr)t[0].attr.hasOwnProperty(s)&&(r+=" "+s+'="'+((m=t[0].attr[s])||0===m?String(m).replace(/&/g,"&").replace(/"/g,"""):"")+'"');r+=">",t[0].children.forEach(function(e){r+=e}),r+="",t[0]=r}return i[0]=t[0],a&&a(t[0]),i}},function(e,t){"use strict";var s=/["'&<>]/;e.exports=function(e){var t,n=""+e,i=s.exec(n);if(!i)return n;var r="",o=0,a=0;for(o=i.index;oe.length)&&e.substring(0,t.length)===t},Rc:function(e,t){if(e===t)return!0;if(11===e.nodeType)return!1;if(t.contains)return t.contains(3===e.nodeType?e.parentNode:e);if(t.compareDocumentPosition)return 16==(16&t.compareDocumentPosition(e));for(;e&&e!=t;)e=e.parentNode;return!!e},qb:function(e){return D.a.Rc(e,e.ownerDocument.documentElement)},Tb:function(e){return!!D.a.Vb(e,D.a.qb)},A:function(e){return e&&e.tagName&&e.tagName.toLowerCase()},Zb:function(e){return D.onError?function(){try{return e.apply(this,arguments)}catch(e){throw D.onError&&D.onError(e),e}}:e},setTimeout:function(e,t){return setTimeout(D.a.Zb(e),t)},dc:function(e){setTimeout(function(){throw D.onError&&D.onError(e),e},0)},q:function(t,e,n){var i=D.a.Zb(n);if(n=l&&s[e],D.options.useOnlyNativeEvents||n||!Nla)if(n||"function"!=typeof t.addEventListener){if(void 0===t.attachEvent)throw Error("Browser doesn't support addEventListener or attachEvent");var r=function(e){i.call(t,e)},o="on"+e;t.attachEvent(o,r),D.a.G.qa(t,function(){t.detachEvent(o,r)})}else t.addEventListener(e,i,!1);else Nla(t).bind(e,i)},Fa:function(e,t){if(!e||!e.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var n;if(n=!("input"!==D.a.A(e)||!e.type||"click"!=t.toLowerCase()||"checkbox"!=(n=e.type)&&"radio"!=n),D.options.useOnlyNativeEvents||!Nla||n)if("function"==typeof Lla.createEvent){if("function"!=typeof e.dispatchEvent)throw Error("The supplied element doesn't support dispatchEvent");(n=Lla.createEvent(a[t]||"HTMLEvents")).initEvent(t,!0,!0,Kla,0,0,0,0,0,!1,!1,!1,!1,0,e),e.dispatchEvent(n)}else if(n&&e.click)e.click();else{if(void 0===e.fireEvent)throw Error("Browser doesn't support triggering events");e.fireEvent("on"+t)}else Nla(e).trigger(t)},c:function(e){return D.I(e)?e():e},Bb:function(e){return D.I(e)?e.p():e},fb:function(t,e,n){var i;e&&("object"==typeof t.classList?(i=t.classList[n?"add":"remove"],D.a.r(e.match(u),function(e){i.call(t.classList,e)})):"string"==typeof t.className.baseVal?r(t.className,"baseVal",e,n):r(t,"className",e,n))},bb:function(e,t){var n=D.a.c(t);null!==n&&n!==Jla||(n="");var i=D.f.firstChild(e);!i||3!=i.nodeType||D.f.nextSibling(i)?D.f.fa(e,[e.ownerDocument.createTextNode(n)]):i.data=n,D.a.Wc(e)},vc:function(e,t){if(e.name=t,l<=7)try{e.mergeAttributes(Lla.createElement(""),!1)}catch(e){}},Wc:function(e){9<=l&&(e=1==e.nodeType?e:e.parentNode).style&&(e.style.zoom=e.style.zoom)},Sc:function(e){if(l){var t=e.style.width;e.style.width=0,e.style.width=t}},nd:function(e,t){e=D.a.c(e),t=D.a.c(t);for(var n=[],i=e;i<=t;i++)n.push(i);return n},W:function(e){for(var t=[],n=0,i=e.length;n",""],tbody:c,tfoot:c,tr:[2,"","
    "],td:d=[3,"","
    "],th:d,option:h=[1,""],optgroup:h},p=D.a.C<=8,D.a.na=function(e,t){var n;if(Nla){if(Nla.parseHTML)n=Nla.parseHTML(e,t)||[];else if((n=Nla.clean([e],t))&&n[0]){for(var i=n[0];i.parentNode&&11!==i.parentNode.nodeType;)i=i.parentNode;i.parentNode&&i.parentNode.removeChild(i)}}else{(n=t)||(n=Lla),i=n.parentWindow||n.defaultView||Kla;var r,o=D.a.cb(e).toLowerCase(),a=n.createElement("div");for(o=(r=(o=o.match(/^<([a-z]+)[ >]/))&&f[o[1]]||u)[0],r="ignored
    "+r[1]+e+r[2]+"
    ","function"==typeof i.innerShiv?a.appendChild(i.innerShiv(r)):(p&&n.appendChild(a),a.innerHTML=r,p&&a.parentNode.removeChild(a));o--;)a=a.lastChild;n=D.a.W(a.lastChild.childNodes)}return n},D.a.Eb=function(e,t){if(D.a.rb(e),null!==(t=D.a.c(t))&&t!==Jla)if("string"!=typeof t&&(t=t.toString()),Nla)Nla(e).html(t);else for(var n=D.a.na(t,e.ownerDocument),i=0;i]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,n=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Tc:function(e,t,n){t.isTemplateRewritten(e,n)||t.rewriteTemplate(e,function(e){return D.Ib.jd(e,t)},n)},jd:function(e,o){return e.replace(t,function(e,t,n,i,r){return a(r,t,n,o)}).replace(n,function(e,t){return a(t,"\x3c!-- ko --\x3e","#comment",o)})},Jc:function(i,r){return D.N.yb(function(e,t){var n=e.nextSibling;n&&n.nodeName.toLowerCase()===r&&D.La(n,i,t)})}}}(),D.b("__tr_ambtns",D.Ib.Jc),function(){D.v={},D.v.n=function(e){if(this.n=e){var t=D.a.A(e);this.eb="script"===t?1:"textarea"===t?2:"template"==t&&e.content&&11===e.content.nodeType?3:4}},D.v.n.prototype.text=function(){var e=1===this.eb?"text":2===this.eb?"value":"innerHTML";if(0==arguments.length)return this.n[e];var t=arguments[0];"innerHTML"===e?D.a.Eb(this.n,t):this.n[e]=t};var t=D.a.e.J()+"_";D.v.n.prototype.data=function(e){if(1===arguments.length)return D.a.e.get(this.n,t+e);D.a.e.set(this.n,t+e,arguments[1])};var n=D.a.e.J();D.v.n.prototype.nodes=function(){var e=this.n;if(0==arguments.length)return(D.a.e.get(e,n)||{}).mb||(3===this.eb?e.content:4===this.eb?e:Jla);D.a.e.set(e,n,{mb:arguments[0]})},D.v.sa=function(e){this.n=e},D.v.sa.prototype=new D.v.n,D.v.sa.prototype.text=function(){if(0==arguments.length){var e=D.a.e.get(this.n,n)||{};return e.Jb===Jla&&e.mb&&(e.Jb=e.mb.innerHTML),e.Jb}D.a.e.set(this.n,n,{Jb:arguments[0]})},D.b("templateSources",D.v),D.b("templateSources.domElement",D.v.n),D.b("templateSources.anonymousTemplate",D.v.sa)}(),function(){function i(e,t,n){var i;for(t=D.f.nextSibling(t);e&&(i=e)!==t;)n(i,e=D.f.nextSibling(i))}function u(e,t){if(e.length){var r=e[0],o=e[e.length-1],n=r.parentNode,a=D.S.instance,s=a.preprocessNode;if(s){if(i(r,o,function(e,t){var n=e.previousSibling,i=s.call(a,e);i&&(e===r&&(r=i[0]||t),e===o&&(o=i[i.length-1]||n))}),e.length=0,!r)return;r===o?e.push(r):(e.push(r,o),D.a.Ba(e,n))}i(r,o,function(e){1!==e.nodeType&&8!==e.nodeType||D.Ub(t,e)}),i(r,o,function(e){1!==e.nodeType&&8!==e.nodeType||D.N.Cc(e,[t])}),D.a.Ba(e,n)}}function l(e){return e.nodeType?e:0"+t+"<\/script>")},0").attr("id",n.containerId).addClass(n.positionClass)).appendTo(T(n.target)),g=g),g;var n}function s(e,t,n){var i=!(!n||!n.force)&&n.force;return!(!e||!i&&0!==T(":focus",e).length||(e[t.hideMethod]({duration:t.hideDuration,easing:t.hideEasing,complete:function(){x(e)}}),0))}function _(e){t&&t(e)}function l(t){var r=w(),e=t.iconClass||r.iconClass;if(void 0!==t.optionsOverride&&(r=T.extend(r,t.optionsOverride),e=t.optionsOverride.iconClass||e),!function(e,t){if(e.preventDuplicates){if(t.message===v)return!0;v=t.message}return!1}(r,t)){y++,g=b(r,!0);var o=null,a=T("
    "),n=T("
    "),i=T("
    "),s=T("
    "),l=T(r.closeHtml),u={intervalId:null,hideEta:null,maxHideTime:null},c={toastId:y,state:"visible",startTime:new Date,options:r,map:t};return t.iconClass&&a.addClass(r.toastClass).addClass(e),function(){if(t.title){var e=t.title;r.escapeHtml&&(e=d(t.title)),n.append(e).addClass(r.titleClass),a.append(n)}}(),function(){if(t.message){var e=t.message;r.escapeHtml&&(e=d(t.message)),i.append(e).addClass(r.messageClass),a.append(i)}}(),r.closeButton&&(l.addClass(r.closeClass).attr("role","button"),a.prepend(l)),r.progressBar&&(s.addClass(r.progressClass),a.prepend(s)),r.rtl&&a.addClass("rtl"),r.newestOnTop?g.prepend(a):g.append(a),function(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}a.attr("aria-live",e)}(),a.hide(),a[r.showMethod]({duration:r.showDuration,easing:r.showEasing,complete:r.onShown}),0/g,">")}function h(e){var t=e&&!1!==r.closeMethod?r.closeMethod:r.hideMethod,n=e&&!1!==r.closeDuration?r.closeDuration:r.hideDuration,i=e&&!1!==r.closeEasing?r.closeEasing:r.hideEasing;if(!T(":focus",a).length||e)return clearTimeout(u.intervalId),a[t]({duration:n,easing:i,complete:function(){x(a),clearTimeout(o),r.onHidden&&"hidden"!==c.state&&r.onHidden(),c.state="hidden",c.endTime=new Date,_(c)}})}function f(){(0×',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1},e.options)}function x(e){g||(g=b()),e.is(":visible")||(e.remove(),e=null,0===g.children().length&&(g.remove(),v=void 0))}}()}),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,r;function y(){return e.apply(null,arguments)}function s(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function l(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function u(e){return void 0===e}function c(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function d(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function h(e,t){var n,i=[];for(n=0;n>>0,i=0;ixe(e)?(o=e+1,s-xe(e)):(o=e,s),{year:o,dayOfYear:a}}function qe(e,t,n){var i,r,o=Re(e.year(),t,n),a=Math.floor((e.dayOfYear()-o-1)/7)+1;return a<1?i=a+Be(r=e.year()-1,t,n):a>Be(e.year(),t,n)?(i=a-Be(e.year(),t,n),r=e.year()+1):(r=e.year(),i=a),{week:i,year:r}}function Be(e,t,n){var i=Re(e,t,n),r=Re(e+1,t,n);return(xe(e)-i+r)/7}B("w",["ww",2],"wo","week"),B("W",["WW",2],"Wo","isoWeek"),O("week","w"),O("isoWeek","W"),j("week",5),j("isoWeek",5),le("w",K),le("ww",K,$),le("W",K),le("WW",K,$),he(["w","ww","W","WW"],function(e,t,n,i){t[i.substr(0,1)]=C(e)}),B("d",0,"do","day"),B("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),B("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),B("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),B("e",0,0,"weekday"),B("E",0,0,"isoWeekday"),O("day","d"),O("weekday","e"),O("isoWeekday","E"),j("day",11),j("weekday",11),j("isoWeekday",11),le("d",K),le("e",K),le("E",K),le("dd",function(e,t){return t.weekdaysMinRegex(e)}),le("ddd",function(e,t){return t.weekdaysShortRegex(e)}),le("dddd",function(e,t){return t.weekdaysRegex(e)}),he(["dd","ddd","dddd"],function(e,t,n,i){var r=n._locale.weekdaysParse(e,i,n._strict);null!=r?t.d=r:_(n).invalidWeekday=e}),he(["d","e","E"],function(e,t,n,i){t[i]=C(e)});var Ve="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),We="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Ue="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),$e=ae,ze=ae,Ge=ae;function Je(){function e(e,t){return t.length-e.length}var t,n,i,r,o,a=[],s=[],l=[],u=[];for(t=0;t<7;t++)n=p([2e3,1]).day(t),i=this.weekdaysMin(n,""),r=this.weekdaysShort(n,""),o=this.weekdays(n,""),a.push(i),s.push(r),l.push(o),u.push(i),u.push(r),u.push(o);for(a.sort(e),s.sort(e),l.sort(e),u.sort(e),t=0;t<7;t++)s[t]=ue(s[t]),l[t]=ue(l[t]),u[t]=ue(u[t]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Ke(){return this.hours()%12||12}function Qe(e,t){B(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function Ze(e,t){return t._meridiemParse}B("H",["HH",2],0,"hour"),B("h",["hh",2],0,Ke),B("k",["kk",2],0,function(){return this.hours()||24}),B("hmm",0,0,function(){return""+Ke.apply(this)+H(this.minutes(),2)}),B("hmmss",0,0,function(){return""+Ke.apply(this)+H(this.minutes(),2)+H(this.seconds(),2)}),B("Hmm",0,0,function(){return""+this.hours()+H(this.minutes(),2)}),B("Hmmss",0,0,function(){return""+this.hours()+H(this.minutes(),2)+H(this.seconds(),2)}),Qe("a",!0),Qe("A",!1),O("hour","h"),j("hour",13),le("a",Ze),le("A",Ze),le("H",K),le("h",K),le("k",K),le("HH",K,$),le("hh",K,$),le("kk",K,$),le("hmm",Q),le("hmmss",Z),le("Hmm",Q),le("Hmmss",Z),de(["H","HH"],ge),de(["k","kk"],function(e,t,n){var i=C(e);t[ge]=24===i?0:i}),de(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),de(["h","hh"],function(e,t,n){t[ge]=C(e),_(n).bigHour=!0}),de("hmm",function(e,t,n){var i=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i)),_(n).bigHour=!0}),de("hmmss",function(e,t,n){var i=e.length-4,r=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i,2)),t[ye]=C(e.substr(r)),_(n).bigHour=!0}),de("Hmm",function(e,t,n){var i=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i))}),de("Hmmss",function(e,t,n){var i=e.length-4,r=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i,2)),t[ye]=C(e.substr(r))});var Xe,et=ke("Hours",!0),tt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ne,monthsShort:Oe,week:{dow:0,doy:6},weekdays:Ve,weekdaysMin:Ue,weekdaysShort:We,meridiemParse:/[ap]\.?m?\.?/i},nt={},it={};function rt(e){return e?e.toLowerCase().replace("_","-"):e}function ot(e){var t=null;if(!nt[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=Xe._abbr,require("./locale/"+e),at(t)}catch(e){}return nt[e]}function at(e,t){var n;return e&&((n=u(t)?lt(e):st(e,t))?Xe=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),Xe._abbr}function st(e,t){if(null===t)return delete nt[e],null;var n,i=tt;if(t.abbr=e,null!=nt[e])E("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),i=nt[e]._config;else if(null!=t.parentLocale)if(null!=nt[t.parentLocale])i=nt[t.parentLocale]._config;else{if(null==(n=ot(t.parentLocale)))return it[t.parentLocale]||(it[t.parentLocale]=[]),it[t.parentLocale].push({name:e,config:t}),null;i=n._config}return nt[e]=new A(M(i,t)),it[e]&&it[e].forEach(function(e){st(e.name,e.config)}),at(e),nt[e]}function lt(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return Xe;if(!s(e)){if(t=ot(e))return t;e=[e]}return function(e){for(var t,n,i,r,o=0;o=t&&a(r,n,!0)>=t-1)break;t--}o++}return Xe}(e)}function ut(e){var t,n=e._a;return n&&-2===_(e).overflow&&(t=n[pe]<0||11Me(n[fe],n[pe])?me:n[ge]<0||24Be(n,o,a)?_(e)._overflowWeeks=!0:null!=l?_(e)._overflowWeekday=!0:(s=Ye(n,i,r,o,a),e._a[fe]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(o=ct(e._a[fe],i[fe]),(e._dayOfYear>xe(o)||0===e._dayOfYear)&&(_(e)._overflowDayOfYear=!0),n=Fe(o,0,e._dayOfYear),e._a[pe]=n.getUTCMonth(),e._a[me]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=i[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[ve]&&0===e._a[ye]&&0===e._a[be]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Fe:function(e,t,n,i,r,o,a){var s=new Date(e,t,n,i,r,o,a);return e<100&&0<=e&&isFinite(s.getFullYear())&&s.setFullYear(e),s}).apply(null,a),r=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(_(e).weekdayMismatch=!0)}}var ht=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ft=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,pt=/Z|[+-]\d\d(?::?\d\d)?/,mt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],gt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],vt=/^\/?Date\((\-?\d+)/i;function yt(e){var t,n,i,r,o,a,s=e._i,l=ht.exec(s)||ft.exec(s);if(l){for(_(e).iso=!0,t=0,n=mt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},sn.isLocal=function(){return!!this.isValid()&&!this._isUTC},sn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},sn.isUtc=Ft,sn.isUTC=Ft,sn.zoneAbbr=function(){return this._isUTC?"UTC":""},sn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},sn.dates=n("dates accessor is deprecated. Use date instead.",en),sn.months=n("months accessor is deprecated. Use month instead",Pe),sn.years=n("years accessor is deprecated. Use year instead",De),sn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),sn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!u(this._isDSTShifted))return this._isDSTShifted;var e={};if(v(e,this),(e=Tt(e))._a){var t=e._isUTC?p(e._a):Dt(e._a);this._isDSTShifted=this.isValid()&&0 div").hide().filter(".datepicker-"+d[this.currentViewMode].CLASS_NAME).show())},o.prototype._isInDisabledDates=function(e){return!0===this._options.disabledDates[e.format("YYYY-MM-DD")]},o.prototype._isInEnabledDates=function(e){return!0===this._options.enabledDates[e.format("YYYY-MM-DD")]},o.prototype._isInDisabledHours=function(e){return!0===this._options.disabledHours[e.format("H")]},o.prototype._isInEnabledHours=function(e){return!0===this._options.enabledHours[e.format("H")]},o.prototype._isValid=function(e,t){if(!e.isValid())return!1;if(this._options.disabledDates&&"d"===t&&this._isInDisabledDates(e))return!1;if(this._options.enabledDates&&"d"===t&&!this._isInEnabledDates(e))return!1;if(this._options.minDate&&e.isBefore(this._options.minDate,t))return!1;if(this._options.maxDate&&e.isAfter(this._options.maxDate,t))return!1;if(this._options.daysOfWeekDisabled&&"d"===t&&-1!==this._options.daysOfWeekDisabled.indexOf(e.day()))return!1;if(this._options.disabledHours&&("h"===t||"m"===t||"s"===t)&&this._isInDisabledHours(e))return!1;if(this._options.enabledHours&&("h"===t||"m"===t||"s"===t)&&!this._isInEnabledHours(e))return!1;if(this._options.disabledTimeIntervals&&("h"===t||"m"===t||"s"===t)){var n=!1;if(r.each(this._options.disabledTimeIntervals,function(){if(e.isBetween(this[0],this[1]))return!(n=!0)}),n)return!1}return!0},o.prototype._parseInputDate=function(e){return void 0===this._options.parseInputDate?n.isMoment(e)||(e=this.getMoment(e)):e=this._options.parseInputDate(e),e},o.prototype._keydown=function(e){var t=null,n=void 0,i=void 0,r=void 0,o=void 0,a=[],s={},l=e.which;for(n in p[l]="p",p)p.hasOwnProperty(n)&&"p"===p[n]&&(a.push(n),parseInt(n,10)!==l&&(s[n]=!0));for(n in this._options.keyBinds)if(this._options.keyBinds.hasOwnProperty(n)&&"function"==typeof this._options.keyBinds[n]&&(r=n.split(" ")).length===a.length&&h[l]===r[r.length-1]){for(o=!0,i=r.length-2;0<=i;i--)if(!(h[r[i]]in s)){o=!1;break}if(o){t=this._options.keyBinds[n];break}}t&&t.call(this)&&(e.stopPropagation(),e.preventDefault())},o.prototype._keyup=function(e){p[e.which]="r",m[e.which]&&(m[e.which]=!1,e.stopPropagation(),e.preventDefault())},o.prototype._indexGivenDates=function(e){var t={},n=this;return r.each(e,function(){var e=n._parseInputDate(this);e.isValid()&&(t[e.format("YYYY-MM-DD")]=!0)}),!!Object.keys(t).length&&t},o.prototype._indexGivenHours=function(e){var t={};return r.each(e,function(){t[this]=!0}),!!Object.keys(t).length&&t},o.prototype._initFormatting=function(){var e=this._options.format||"L LT",t=this;this.actualFormat=e.replace(/(\[[^\[]*])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(e){return t._dates[0].localeData().longDateFormat(e)||e}),this.parseFormats=this._options.extraFormats?this._options.extraFormats.slice():[],this.parseFormats.indexOf(e)<0&&this.parseFormats.indexOf(this.actualFormat)<0&&this.parseFormats.push(this.actualFormat),this.use24Hours=this.actualFormat.toLowerCase().indexOf("a")<1&&this.actualFormat.replace(/\[.*?]/g,"").indexOf("h")<1,this._isEnabled("y")&&(this.MinViewModeNumber=2),this._isEnabled("M")&&(this.MinViewModeNumber=1),this._isEnabled("d")&&(this.MinViewModeNumber=0),this.currentViewMode=Math.max(this.MinViewModeNumber,this.currentViewMode),this.unset||this._setValue(this._dates[0],0)},o.prototype._getLastPickedDate=function(){return this._dates[this._getLastPickedDateIndex()]},o.prototype._getLastPickedDateIndex=function(){return this._dates.length-1},o.prototype.getMoment=function(e){var t=void 0;return t=null==e?n():this._hasTimeZone()?n.tz(e,this.parseFormats,this._options.locale,this._options.useStrict,this._options.timeZone):n(e,this.parseFormats,this._options.locale,this._options.useStrict),this._hasTimeZone()&&t.tz(this._options.timeZone),t},o.prototype.toggle=function(){return this.widget?this.hide():this.show()},o.prototype.ignoreReadonly=function(e){if(0===arguments.length)return this._options.ignoreReadonly;if("boolean"!=typeof e)throw new TypeError("ignoreReadonly () expects a boolean parameter");this._options.ignoreReadonly=e},o.prototype.options=function(e){if(0===arguments.length)return r.extend(!0,{},this._options);if(!(e instanceof Object))throw new TypeError("options() this.options parameter should be an object");r.extend(!0,this._options,e);var n=this;r.each(this._options,function(e,t){void 0!==n[e]&&n[e](t)})},o.prototype.date=function(e,t){if(t=t||0,0===arguments.length)return this.unset?null:this._options.allowMultidate?this._dates.join(this._options.multidateSeparator):this._dates[t].clone();if(!(null===e||"string"==typeof e||n.isMoment(e)||e instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");this._setValue(null===e?null:this._parseInputDate(e),t)},o.prototype.format=function(e){if(0===arguments.length)return this._options.format;if("string"!=typeof e&&("boolean"!=typeof e||!1!==e))throw new TypeError("format() expects a string or boolean:false parameter "+e);this._options.format=e,this.actualFormat&&this._initFormatting()},o.prototype.timeZone=function(e){if(0===arguments.length)return this._options.timeZone;if("string"!=typeof e)throw new TypeError("newZone() expects a string parameter");this._options.timeZone=e},o.prototype.dayViewHeaderFormat=function(e){if(0===arguments.length)return this._options.dayViewHeaderFormat;if("string"!=typeof e)throw new TypeError("dayViewHeaderFormat() expects a string parameter");this._options.dayViewHeaderFormat=e},o.prototype.extraFormats=function(e){if(0===arguments.length)return this._options.extraFormats;if(!1!==e&&!(e instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");this._options.extraFormats=e,this.parseFormats&&this._initFormatting()},o.prototype.disabledDates=function(e){if(0===arguments.length)return this._options.disabledDates?r.extend({},this._options.disabledDates):this._options.disabledDates;if(!e)return this._options.disabledDates=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("disabledDates() expects an array parameter");this._options.disabledDates=this._indexGivenDates(e),this._options.enabledDates=!1,this._update()},o.prototype.enabledDates=function(e){if(0===arguments.length)return this._options.enabledDates?r.extend({},this._options.enabledDates):this._options.enabledDates;if(!e)return this._options.enabledDates=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("enabledDates() expects an array parameter");this._options.enabledDates=this._indexGivenDates(e),this._options.disabledDates=!1,this._update()},o.prototype.daysOfWeekDisabled=function(e){if(0===arguments.length)return this._options.daysOfWeekDisabled.splice(0);if("boolean"==typeof e&&!e)return this._options.daysOfWeekDisabled=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");if(this._options.daysOfWeekDisabled=e.reduce(function(e,t){return 6<(t=parseInt(t,10))||t<0||isNaN(t)||-1===e.indexOf(t)&&e.push(t),e},[]).sort(),this._options.useCurrent&&!this._options.keepInvalid)for(var t=0;t").append(k("").append(k("").addClass("prev").attr("data-action","previous").append(k("").addClass(this._options.icons.previous))).append(k("").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",this._options.calendarWeeks?"6":"5")).append(k("").addClass("next").attr("data-action","next").append(k("").addClass(this._options.icons.next)))),t=k("").append(k("").append(k("").attr("colspan",this._options.calendarWeeks?"8":"7")));return[k("
    ").addClass("datepicker-days").append(k("").addClass("table table-sm").append(e).append(k(""))),k("
    ").addClass("datepicker-months").append(k("
    ").addClass("table-condensed").append(e.clone()).append(t.clone())),k("
    ").addClass("datepicker-years").append(k("
    ").addClass("table-condensed").append(e.clone()).append(t.clone())),k("
    ").addClass("datepicker-decades").append(k("
    ").addClass("table-condensed").append(e.clone()).append(t.clone()))]},r.prototype._getTimePickerMainTemplate=function(){var e=k(""),t=k(""),n=k("");return this._isEnabled("h")&&(e.append(k("").append(k("").append(k("").append(k("
    ").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementHour}).addClass("btn").attr("data-action","incrementHours").append(k("").addClass(this._options.icons.up)))),t.append(k("").append(k("").addClass("timepicker-hour").attr({"data-time-component":"hours",title:this._options.tooltips.pickHour}).attr("data-action","showHours"))),n.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementHour}).addClass("btn").attr("data-action","decrementHours").append(k("").addClass(this._options.icons.down))))),this._isEnabled("m")&&(this._isEnabled("h")&&(e.append(k("").addClass("separator")),t.append(k("").addClass("separator").html(":")),n.append(k("").addClass("separator"))),e.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementMinute}).addClass("btn").attr("data-action","incrementMinutes").append(k("").addClass(this._options.icons.up)))),t.append(k("").append(k("").addClass("timepicker-minute").attr({"data-time-component":"minutes",title:this._options.tooltips.pickMinute}).attr("data-action","showMinutes"))),n.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementMinute}).addClass("btn").attr("data-action","decrementMinutes").append(k("").addClass(this._options.icons.down))))),this._isEnabled("s")&&(this._isEnabled("m")&&(e.append(k("").addClass("separator")),t.append(k("").addClass("separator").html(":")),n.append(k("").addClass("separator"))),e.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementSecond}).addClass("btn").attr("data-action","incrementSeconds").append(k("").addClass(this._options.icons.up)))),t.append(k("").append(k("").addClass("timepicker-second").attr({"data-time-component":"seconds",title:this._options.tooltips.pickSecond}).attr("data-action","showSeconds"))),n.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementSecond}).addClass("btn").attr("data-action","decrementSeconds").append(k("").addClass(this._options.icons.down))))),this.use24Hours||(e.append(k("").addClass("separator")),t.append(k("").append(k("").addClass("separator"))),k("
    ").addClass("timepicker-picker").append(k("").addClass("table-condensed").append([e,t,n]))},r.prototype._getTimePickerTemplate=function(){var e=k("
    ").addClass("timepicker-hours").append(k("
    ").addClass("table-condensed")),t=k("
    ").addClass("timepicker-minutes").append(k("
    ").addClass("table-condensed")),n=k("
    ").addClass("timepicker-seconds").append(k("
    ").addClass("table-condensed")),i=[this._getTimePickerMainTemplate()];return this._isEnabled("h")&&i.push(e),this._isEnabled("m")&&i.push(t),this._isEnabled("s")&&i.push(n),i},r.prototype._getToolbar=function(){var e=[];if(this._options.buttons.showToday&&e.push(k("
    ").append(k("").attr({href:"#",tabindex:"-1","data-action":"today",title:this._options.tooltips.today}).append(k("").addClass(this._options.icons.today)))),!this._options.sideBySide&&this._hasDate()&&this._hasTime()){var t=void 0,n=void 0;n="times"===this._options.viewMode?(t=this._options.tooltips.selectDate,this._options.icons.date):(t=this._options.tooltips.selectTime,this._options.icons.time),e.push(k("").append(k("").attr({href:"#",tabindex:"-1","data-action":"togglePicker",title:t}).append(k("").addClass(n))))}return this._options.buttons.showClear&&e.push(k("").append(k("").attr({href:"#",tabindex:"-1","data-action":"clear",title:this._options.tooltips.clear}).append(k("").addClass(this._options.icons.clear)))),this._options.buttons.showClose&&e.push(k("").append(k("").attr({href:"#",tabindex:"-1","data-action":"close",title:this._options.tooltips.close}).append(k("").addClass(this._options.icons.close)))),0===e.length?"":k("").addClass("table-condensed").append(k("").append(k("").append(e)))},r.prototype._getTemplate=function(){var e=k("
    ").addClass("bootstrap-datetimepicker-widget dropdown-menu"),t=k("
    ").addClass("datepicker").append(this._getDatePickerTemplate()),n=k("
    ").addClass("timepicker").append(this._getTimePickerTemplate()),i=k("
      ").addClass("list-unstyled"),r=k("
    • ").addClass("picker-switch"+(this._options.collapse?" accordion-toggle":"")).append(this._getToolbar());return this._options.inline&&e.removeClass("dropdown-menu"),this.use24Hours&&e.addClass("usetwentyfour"),this._isEnabled("s")&&!this.use24Hours&&e.addClass("wider"),this._options.sideBySide&&this._hasDate()&&this._hasTime()?(e.addClass("timepicker-sbs"),"top"===this._options.toolbarPlacement&&e.append(r),e.append(k("
      ").addClass("row").append(t.addClass("col-md-6")).append(n.addClass("col-md-6"))),"bottom"!==this._options.toolbarPlacement&&"default"!==this._options.toolbarPlacement||e.append(r),e):("top"===this._options.toolbarPlacement&&i.append(r),this._hasDate()&&i.append(k("
    • ").addClass(this._options.collapse&&this._hasTime()?"collapse":"").addClass(this._options.collapse&&this._hasTime()&&"times"===this._options.viewMode?"":"show").append(t)),"default"===this._options.toolbarPlacement&&i.append(r),this._hasTime()&&i.append(k("
    • ").addClass(this._options.collapse&&this._hasDate()?"collapse":"").addClass(this._options.collapse&&this._hasDate()&&"times"===this._options.viewMode?"show":"").append(n)),"bottom"===this._options.toolbarPlacement&&i.append(r),e.append(i))},r.prototype._place=function(e){var t=e&&e.data&&e.data.picker||this,n=t._options.widgetPositioning.vertical,i=t._options.widgetPositioning.horizontal,r=void 0,o=(t.component&&t.component.length?t.component:t._element).position(),a=(t.component&&t.component.length?t.component:t._element).offset();if(t._options.widgetParent)r=t._options.widgetParent.append(t.widget);else if(t._element.is("input"))r=t._element.after(t.widget).parent();else{if(t._options.inline)return void(r=t._element.append(t.widget));r=t._element,t._element.children().first().after(t.widget)}if("auto"===n&&(n=a.top+1.5*t.widget.height()>=k(window).height()+k(window).scrollTop()&&t.widget.height()+t._element.outerHeight()k(window).width()?"right":"left"),"top"===n?t.widget.addClass("top").removeClass("bottom"):t.widget.addClass("bottom").removeClass("top"),"right"===i?t.widget.addClass("float-right"):t.widget.removeClass("float-right"),"relative"!==r.css("position")&&(r=r.parents().filter(function(){return"relative"===k(this).css("position")}).first()),0===r.length)throw new Error("datetimepicker component should be placed within a relative positioned container");t.widget.css({top:"top"===n?"auto":o.top+t._element.outerHeight()+"px",bottom:"top"===n?r.outerHeight()-(r===t._element?0:o.top)+"px":"auto",left:"left"===i?(r===t._element?0:o.left)+"px":"auto",right:"left"===i?"auto":r.outerWidth()-t._element.outerWidth()-(r===t._element?0:o.left)+"px"})},r.prototype._fillDow=function(){var e=k("
    "),t=this._viewDate.clone().startOf("w").startOf("d");for(!0===this._options.calendarWeeks&&e.append(k(""),this._options.calendarWeeks&&r.append('"),n.push(r)),o="",i.isBefore(this._viewDate,"M")&&(o+=" old"),i.isAfter(this._viewDate,"M")&&(o+=" new"),this._options.allowMultidate){var s=this._datesFormatted.indexOf(i.format("YYYY-MM-DD"));-1!==s&&i.isSame(this._datesFormatted[s],"d")&&!this.unset&&(o+=" active")}else i.isSame(this._getLastPickedDate(),"d")&&!this.unset&&(o+=" active");this._isValid(i,"d")||(o+=" disabled"),i.isSame(this.getMoment(),"d")&&(o+=" today"),0!==i.day()&&6!==i.day()||(o+=" weekend"),r.append('"),i.add(1,"d")}e.find("tbody").empty().append(n),this._updateMonths(),this._updateYears(),this._updateDecades()}},r.prototype._fillHours=function(){var e=this.widget.find(".timepicker-hours table"),t=this._viewDate.clone().startOf("d"),n=[],i=k("");for(11"),n.push(i)),i.append('"),t.add(1,"h");e.empty().append(n)},r.prototype._fillMinutes=function(){for(var e=this.widget.find(".timepicker-minutes table"),t=this._viewDate.clone().startOf("h"),n=[],i=1===this._options.stepping?5:this._options.stepping,r=k("");this._viewDate.isSame(t,"h");)t.minute()%(4*i)==0&&(r=k(""),n.push(r)),r.append('"),t.add(i,"m");e.empty().append(n)},r.prototype._fillSeconds=function(){for(var e=this.widget.find(".timepicker-seconds table"),t=this._viewDate.clone().startOf("m"),n=[],i=k("");this._viewDate.isSame(t,"m");)t.second()%20==0&&(i=k(""),n.push(i)),i.append('"),t.add(5,"s");e.empty().append(n)},r.prototype._fillTime=function(){var e=void 0,t=void 0,n=this.widget.find(".timepicker span[data-time-component]");this.use24Hours||(e=this.widget.find(".timepicker [data-action=togglePeriod]"),t=this._getLastPickedDate().clone().add(12<=this._getLastPickedDate().hours()?-12:12,"h"),e.text(this._getLastPickedDate().format("A")),this._isValid(t,"h")?e.removeClass("disabled"):e.addClass("disabled")),n.filter("[data-time-component=hours]").text(this._getLastPickedDate().format(this.use24Hours?"HH":"hh")),n.filter("[data-time-component=minutes]").text(this._getLastPickedDate().format("mm")),n.filter("[data-time-component=seconds]").text(this._getLastPickedDate().format("ss")),this._fillHours(),this._fillMinutes(),this._fillSeconds()},r.prototype._doAction=function(e,t){var n=this._getLastPickedDate();if(k(e.currentTarget).is(".disabled"))return!1;switch(t=t||k(e.currentTarget).data("action")){case"next":var i=E.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.add(E.DatePickerModes[this.currentViewMode].NAV_STEP,i),this._fillDate(),this._viewUpdate(i);break;case"previous":var r=E.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.subtract(E.DatePickerModes[this.currentViewMode].NAV_STEP,r),this._fillDate(),this._viewUpdate(r);break;case"pickerSwitch":this._showMode(1);break;case"selectMonth":var o=k(e.target).closest("tbody").find("span").index(k(e.target));this._viewDate.month(o),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()).month(this._viewDate.month()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("M");break;case"selectYear":var a=parseInt(k(e.target).text(),10)||0;this._viewDate.year(a),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDecade":var s=parseInt(k(e.target).data("selection"),10)||0;this._viewDate.year(s),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDay":var l=this._viewDate.clone();k(e.target).is(".old")&&l.subtract(1,"M"),k(e.target).is(".new")&&l.add(1,"M");var u=l.date(parseInt(k(e.target).text(),10)),c=0;this._options.allowMultidate?-1!==(c=this._datesFormatted.indexOf(u.format("YYYY-MM-DD")))?this._setValue(null,c):this._setValue(u,this._getLastPickedDateIndex()+1):this._setValue(u,this._getLastPickedDateIndex()),this._hasTime()||this._options.keepOpen||this._options.inline||this._options.allowMultidate||this.hide();break;case"incrementHours":var d=n.clone().add(1,"h");this._isValid(d,"h")&&this._setValue(d,this._getLastPickedDateIndex());break;case"incrementMinutes":var h=n.clone().add(this._options.stepping,"m");this._isValid(h,"m")&&this._setValue(h,this._getLastPickedDateIndex());break;case"incrementSeconds":var f=n.clone().add(1,"s");this._isValid(f,"s")&&this._setValue(f,this._getLastPickedDateIndex());break;case"decrementHours":var p=n.clone().subtract(1,"h");this._isValid(p,"h")&&this._setValue(p,this._getLastPickedDateIndex());break;case"decrementMinutes":var m=n.clone().subtract(this._options.stepping,"m");this._isValid(m,"m")&&this._setValue(m,this._getLastPickedDateIndex());break;case"decrementSeconds":var g=n.clone().subtract(1,"s");this._isValid(g,"s")&&this._setValue(g,this._getLastPickedDateIndex());break;case"togglePeriod":this._setValue(n.clone().add(12<=n.hours()?-12:12,"h"),this._getLastPickedDateIndex());break;case"togglePicker":var v=k(e.target),y=v.closest("a"),b=v.closest("ul"),_=b.find(".show"),w=b.find(".collapse:not(.show)"),x=v.is("span")?v:v.find("span"),T=void 0;if(_&&_.length){if((T=_.data("collapse"))&&T.transitioning)return!0;_.collapse?(_.collapse("hide"),w.collapse("show")):(_.removeClass("show"),w.addClass("show")),x.toggleClass(this._options.icons.time+" "+this._options.icons.date),x.hasClass(this._options.icons.date)?y.attr("title",this._options.tooltips.selectDate):y.attr("title",this._options.tooltips.selectTime)}break;case"showPicker":this.widget.find(".timepicker > div:not(.timepicker-picker)").hide(),this.widget.find(".timepicker .timepicker-picker").show();break;case"showHours":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-hours").show();break;case"showMinutes":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-minutes").show();break;case"showSeconds":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-seconds").show();break;case"selectHour":var C=parseInt(k(e.target).text(),10);this.use24Hours||(12<=n.hours()?12!==C&&(C+=12):12===C&&(C=0)),this._setValue(n.clone().hours(C),this._getLastPickedDateIndex()),this._isEnabled("a")||this._isEnabled("m")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"selectMinute":this._setValue(n.clone().minutes(parseInt(k(e.target).text(),10)),this._getLastPickedDateIndex()),this._isEnabled("a")||this._isEnabled("s")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"selectSecond":this._setValue(n.clone().seconds(parseInt(k(e.target).text(),10)),this._getLastPickedDateIndex()),this._isEnabled("a")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"clear":this.clear();break;case"close":this.hide();break;case"today":var D=this.getMoment();this._isValid(D,"d")&&this._setValue(D,this._getLastPickedDateIndex())}return!1},r.prototype.hide=function(){var t=!1;this.widget&&(this.widget.find(".collapse").each(function(){var e=k(this).data("collapse");return!e||!e.transitioning||!(t=!0)}),t||(this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this.widget.hide(),k(window).off("resize",this._place()),this.widget.off("click","[data-action]"),this.widget.off("mousedown",!1),this.widget.remove(),this.widget=!1,this._notifyEvent({type:E.Event.HIDE,date:this._getLastPickedDate().clone()}),void 0!==this.input&&this.input.blur(),this._viewDate=this._getLastPickedDate().clone()))},r.prototype.show=function(){var e=void 0,t={year:function(e){return e.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(e){return e.date(1).hours(0).seconds(0).minutes(0)},day:function(e){return e.hours(0).seconds(0).minutes(0)},hour:function(e){return e.seconds(0).minutes(0)},minute:function(e){return e.seconds(0)}};if(void 0!==this.input){if(this.input.prop("disabled")||!this._options.ignoreReadonly&&this.input.prop("readonly")||this.widget)return;void 0!==this.input.val()&&0!==this.input.val().trim().length?this._setValue(this._parseInputDate(this.input.val().trim()),0):this.unset&&this._options.useCurrent&&(e=this.getMoment(),"string"==typeof this._options.useCurrent&&(e=t[this._options.useCurrent](e)),this._setValue(e,0))}else this.unset&&this._options.useCurrent&&(e=this.getMoment(),"string"==typeof this._options.useCurrent&&(e=t[this._options.useCurrent](e)),this._setValue(e,0));this.widget=this._getTemplate(),this._fillDow(),this._fillMonths(),this.widget.find(".timepicker-hours").hide(),this.widget.find(".timepicker-minutes").hide(),this.widget.find(".timepicker-seconds").hide(),this._update(),this._showMode(),k(window).on("resize",{picker:this},this._place),this.widget.on("click","[data-action]",k.proxy(this._doAction,this)),this.widget.on("mousedown",!1),this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this._place(),this.widget.show(),void 0!==this.input&&this._options.focusOnShow&&!this.input.is(":focus")&&this.input.focus(),this._notifyEvent({type:E.Event.SHOW})},r.prototype.destroy=function(){this.hide(),this._element.removeData(E.DATA_KEY),this._element.removeData("date")},r.prototype.disable=function(){this.hide(),this.component&&this.component.hasClass("btn")&&this.component.addClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!0)},r.prototype.enable=function(){this.component&&this.component.hasClass("btn")&&this.component.removeClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!1)},r.prototype.toolbarPlacement=function(e){if(0===arguments.length)return this._options.toolbarPlacement;if("string"!=typeof e)throw new TypeError("toolbarPlacement() expects a string parameter");if(-1===_.indexOf(e))throw new TypeError("toolbarPlacement() parameter must be one of ("+_.join(", ")+") value");this._options.toolbarPlacement=e,this.widget&&(this.hide(),this.show())},r.prototype.widgetPositioning=function(e){if(0===arguments.length)return k.extend({},this._options.widgetPositioning);if("[object Object]"!=={}.toString.call(e))throw new TypeError("widgetPositioning() expects an object variable");if(e.horizontal){if("string"!=typeof e.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(e.horizontal=e.horizontal.toLowerCase(),-1===b.indexOf(e.horizontal))throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+b.join(", ")+")");this._options.widgetPositioning.horizontal=e.horizontal}if(e.vertical){if("string"!=typeof e.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(e.vertical=e.vertical.toLowerCase(),-1===y.indexOf(e.vertical))throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+y.join(", ")+")");this._options.widgetPositioning.vertical=e.vertical}this._update()},r.prototype.widgetParent=function(e){if(0===arguments.length)return this._options.widgetParent;if("string"==typeof e&&(e=k(e)),null!==e&&"string"!=typeof e&&!(e instanceof k))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");this._options.widgetParent=e,this.widget&&(this.hide(),this.show())},r._jQueryHandleThis=function(e,t,n){var i=k(e).data(E.DATA_KEY);if("object"===(void 0===t?"undefined":o(t))&&k.extend({},E.Default,t),i||(i=new r(k(e),t),k(e).data(E.DATA_KEY,i)),"string"==typeof t){if(void 0===i[t])throw new Error('No method named "'+t+'"');return void 0===n?i[t]():i[t](n)}},r._jQueryInterface=function(e,t){return 1===this.length?r._jQueryHandleThis(this[0],e,t):this.each(function(){r._jQueryHandleThis(this,e,t)})},r}(E),k(document).on(E.Event.CLICK_DATA_API,E.Selector.DATA_TOGGLE,function(){var e=w(k(this));0!==e.length&&x._jQueryInterface.call(e,"toggle")}).on(E.Event.CHANGE,"."+E.ClassName.INPUT,function(e){var t=w(k(this));0!==t.length&&x._jQueryInterface.call(t,"_change",e)}).on(E.Event.BLUR,"."+E.ClassName.INPUT,function(e){var t=w(k(this)),n=t.data(E.DATA_KEY);0!==t.length&&(n._options.debug||window.debug||x._jQueryInterface.call(t,"hide",e))}).on(E.Event.KEYDOWN,"."+E.ClassName.INPUT,function(e){var t=w(k(this));0!==t.length&&x._jQueryInterface.call(t,"_keydown",e)}).on(E.Event.KEYUP,"."+E.ClassName.INPUT,function(e){var t=w(k(this));0!==t.length&&x._jQueryInterface.call(t,"_keyup",e)}).on(E.Event.FOCUS,"."+E.ClassName.INPUT,function(e){var t=w(k(this)),n=t.data(E.DATA_KEY);0!==t.length&&n._options.allowInputToggle&&x._jQueryInterface.call(t,"show",e)}),k.fn[E.NAME]=x._jQueryInterface,k.fn[E.NAME].Constructor=x,k.fn[E.NAME].noConflict=function(){return k.fn[E.NAME]=v,x._jQueryInterface}}();var Menu={init:function(){$(function(){Menu.itemClick()})},itemClick:function(){$(".menu-button").click(function(e){e.preventDefault(),$(".menu-item").is(":visible")?$(".menu-item").css("display",""):$(".menu-item").show()})}};Menu.init(),ko.components.register("picker",{viewModel:function(n){var i=this;this.textTerm=ko.observable("").extend({rateLimit:500}),this.minSearchText=ko.observable(n.minSearchText||2),this.multipleSelect=ko.observable(n.multipleSelect||!1),this.searchInputPlaceholder=ko.observable(n.searchInputPlaceholder||"Enter "+this.minSearchText()+" or more characters"),this.selectedItemsTitle=ko.observable(n.selectedItemsTitle||"Selected: "),this.searchResultTitle=ko.observable(n.searchResultTitle||"Search result: "),this.suggestedItemsTitle=ko.observable(n.suggestedItemsTitle||"Suggested items: "),this.noItemSelectedTitle=ko.observable(n.noItemSelectedTitle||"No item/s selected"),this.showAllItemsTitle=ko.observable(n.showAllItemsTitle||"more"),this.allowSuggestedItems=ko.observable(n.allowSuggestedItems&&n.url||!1),this.topSuggestedItems=ko.observable(n.topSuggestedItems||5),this.allowItemAlreadySelectedNotification=ko.observable(n.allowItemAlreadySelectedNotification||!0),this.itemAlreadySelectedTitle=ko.observable(n.itemAlreadySelectedTitle||"item already selected"),this.searchResult=ko.observableArray([]),this.selectedResult=ko.observableArray(n.selectedItems||[]),this.suggestedResult=ko.observableArray([]),this.loading=ko.observable(!1);var e=ko.toJSON(this.selectedResult);!0===this.multipleSelect()?0===this.selectedResult().length?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(e):0===this.selectedResult().length?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(this.selectedResult()[0]),this.textTerm.subscribe(function(t){""===t.trim()&&i.searchResult([]),""!==t.trim()&&t.trim().length>=i.minSearchText()&&(n.url?(i.loading(!0),$.get(n.url+"="+t,function(e){-1===e.indexOf(t)&&e.push(t),i.searchResult(e),i.loading(!1)})):i.searchResult([t]))}),this.notify=function(e){toastr.options.closeButton=!0,toastr.options.preventDuplicates=!0,toastr.info(e+" "+this.itemAlreadySelectedTitle())},this.add=function(e){e=e.replace(/'/g,"").replace(/"/g,""),-1

    Loading..

    \x3c!-- ko foreach: suggestedResult --\x3e\x3c!-- /ko --\x3e
    '}),ko.applyBindings(),Holder.addTheme("thumb",{bg:"#55595c",fg:"#eceeef",text:"Thumbnail"});var FormMvc={allowValidateHiddenField:function(e){e.data("validator").settings.ignore=""},disableEnter:function(e){e.on("keyup keypress",function(e){if(13===(e.keyCode||e.which))return e.preventDefault(),!1})}};$(function(){$(".single-select").removeAttr("multiple"),$('[data-toggle="tooltip"]').tooltip()});var JSONTree=function(){var t={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},e=0,n=0;this.create=function(e,t){return n+=1,p(s(e,0,!1),{class:"jstValue"})};var o=function(e){return e.replace(/[&<>'"]/g,function(e){return t[e]})},a=function(){return n+"_"+e++},s=function(e,t,n){if(null===e)return d(n?t:0);switch(typeof e){case"boolean":return c(e,n?t:0);case"number":return u(e,n?t:0);case"string":return l(e,n?t:0);default:return e instanceof Array?r(e,t,n):i(e,t,n)}},i=function(t,n,e){var i=a(),r=Object.keys(t).map(function(e){return h(e,t[e],n+1,!0)}).join(f()),o=[g("{",e?n:0,i),p(r,{id:i}),v("}",n)].join("\n");return p(o,{})},r=function(e,t,n){var i=a(),r=e.map(function(e){return s(e,t+1,!0)}).join(f());return[g("[",n?t:0,i),p(r,{id:i}),v("]",t)].join("\n")},l=function(e,t){var n=o(JSON.stringify(e));return p(y(n,t),{class:"jstStr"})},u=function(e,t){return p(y(e,t),{class:"jstNum"})},c=function(e,t){return p(y(e,t),{class:"jstBool"})},d=function(e){return p(y("null",e),{class:"jstNull"})},h=function(e,t,n){var i=y(o(JSON.stringify(e))+": ",n),r=p(s(t,n,!1),{});return p(i+r,{class:"jstProperty"})},f=function(){return p(",\n",{class:"jstComma"})},p=function(e,t){return m("span",t,e)},m=function(e,t,n){return"<"+e+Object.keys(t).map(function(e){return" "+e+'="'+t[e]+'"'}).join("")+">"+n+""},g=function(e,t,n){return p(y(e,t),{class:"jstBracket"})+p("",{class:"jstFold",onclick:"JSONTree.toggle('"+n+"')"})};this.toggle=function(e){var t=document.getElementById(e),n=t.parentNode,i=t.previousElementSibling;""===t.className?(t.className="jstHiddenBlock",n.className="jstFolded",i.className="jstExpand"):(t.className="",n.className="",i.className="jstFold")};var v=function(e,t){return p(y(e,t),{})},y=function(e,t){return Array(2*t+1).join(" ")+e};return this}();$(function(){$(".local-datetime").each(function(){var e=$(this),t=parseInt(e.attr("data-utc"),10)||0;if(t){var n=moment.utc(t).local().format("DD MMM YYYY HH:mm");e.text(n)}}),$('[data-toggle="tooltip"]').tooltip()}),$(function(){$(".row-error-detail>td").each(function(){var e=$(this).data("error-json"),t=JSONTree.create(JSON.parse(e));$(this).html(t)}),$(".btn-error-detail").click(function(e){e.preventDefault();var t=$(this).data("error-id");return $(".row-error-detail[data-error-id="+t+"]").is(":visible")?$(".row-error-detail[data-error-id="+t+"]").addClass("d-none"):$(".row-error-detail[data-error-id="+t+"]").removeClass("d-none"),!1})}); \ No newline at end of file +if(function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(T,e){"use strict";var t=[],C=T.document,i=Object.getPrototypeOf,s=t.slice,m=t.concat,l=t.push,r=t.indexOf,n={},o=n.toString,g=n.hasOwnProperty,a=g.toString,u=a.call(Object),v={},y=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},b=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,noModule:!0};function _(e,t,n){var i,r=(t=t||C).createElement("script");if(r.text=e,n)for(i in c)n[i]&&(r[i]=n[i]);t.head.appendChild(r).parentNode.removeChild(r)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var d="3.3.1",D=function(e,t){return new D.fn.init(e,t)},h=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function f(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!y(e)&&!b(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+j+")"+j+"*"),W=new RegExp("="+j+"*([^\\]'\"]*?)"+j+"*\\]","g"),U=new RegExp(R),$=new RegExp("^"+H+"$"),z={ID:new RegExp("^#("+H+")"),CLASS:new RegExp("^\\.("+H+")"),TAG:new RegExp("^("+H+"|[*])"),ATTR:new RegExp("^"+F),PSEUDO:new RegExp("^"+R),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+j+"*(even|odd|(([+-]|)(\\d*)n|)"+j+"*(?:([+-]|)"+j+"*(\\d+)|))"+j+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+j+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+j+"*((?:-\\d)?\\d*)"+j+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Z=/[+~]/,X=new RegExp("\\\\([\\da-f]{1,6}"+j+"?|("+j+")|.)","ig"),ee=function(e,t,n){var i="0x"+t-65536;return i!=i||n?t:i<0?String.fromCharCode(i+65536):String.fromCharCode(i>>10|55296,1023&i|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},ie=function(){x()},re=ye(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{O.apply(t=I.call(y.childNodes),y.childNodes),t[y.childNodes.length].nodeType}catch(e){O={apply:t.length?function(e,t){N.apply(e,I.call(t))}:function(e,t){for(var n=e.length,i=0;e[n++]=t[i++];);e.length=n-1}}}function oe(e,t,n,i){var r,o,a,s,l,u,c,d=t&&t.ownerDocument,h=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==h&&9!==h&&11!==h)return n;if(!i&&((t?t.ownerDocument||t:y)!==T&&x(t),t=t||T,C)){if(11!==h&&(l=Q.exec(e)))if(r=l[1]){if(9===h){if(!(a=t.getElementById(r)))return n;if(a.id===r)return n.push(a),n}else if(d&&(a=d.getElementById(r))&&v(t,a)&&a.id===r)return n.push(a),n}else{if(l[2])return O.apply(n,t.getElementsByTagName(e)),n;if((r=l[3])&&f.getElementsByClassName&&t.getElementsByClassName)return O.apply(n,t.getElementsByClassName(r)),n}if(f.qsa&&!E[e+" "]&&(!g||!g.test(e))){if(1!==h)d=t,c=e;else if("object"!==t.nodeName.toLowerCase()){for((s=t.getAttribute("id"))?s=s.replace(te,ne):t.setAttribute("id",s=D),o=(u=p(e)).length;o--;)u[o]="#"+s+" "+ve(u[o]);c=u.join(","),d=Z.test(e)&&me(t.parentNode)||t}if(c)try{return O.apply(n,d.querySelectorAll(c)),n}catch(e){}finally{s===D&&t.removeAttribute("id")}}}return m(e.replace(q,"$1"),t,n,i)}function ae(){var i=[];return function e(t,n){return i.push(t+" ")>_.cacheLength&&delete e[i.shift()],e[t+" "]=n}}function se(e){return e[D]=!0,e}function le(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ue(e,t){for(var n=e.split("|"),i=n.length;i--;)_.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,i=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(i)return i;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function fe(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&re(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function pe(a){return se(function(o){return o=+o,se(function(e,t){for(var n,i=a([],e.length,o),r=i.length;r--;)e[n=i[r]]&&(e[n]=!(t[n]=e[n]))})})}function me(e){return e&&void 0!==e.getElementsByTagName&&e}for(e in f=oe.support={},r=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},x=oe.setDocument=function(e){var t,n,i=e?e.ownerDocument||e:y;return i!==T&&9===i.nodeType&&i.documentElement&&(a=(T=i).documentElement,C=!r(T),y!==T&&(n=T.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",ie,!1):n.attachEvent&&n.attachEvent("onunload",ie)),f.attributes=le(function(e){return e.className="i",!e.getAttribute("className")}),f.getElementsByTagName=le(function(e){return e.appendChild(T.createComment("")),!e.getElementsByTagName("*").length}),f.getElementsByClassName=K.test(T.getElementsByClassName),f.getById=le(function(e){return a.appendChild(e).id=D,!T.getElementsByName||!T.getElementsByName(D).length}),f.getById?(_.filter.ID=function(e){var t=e.replace(X,ee);return function(e){return e.getAttribute("id")===t}},_.find.ID=function(e,t){if(void 0!==t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(_.filter.ID=function(e){var n=e.replace(X,ee);return function(e){var t=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},_.find.ID=function(e,t){if(void 0!==t.getElementById&&C){var n,i,r,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(r=t.getElementsByName(e),i=0;o=r[i++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),_.find.TAG=f.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):f.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,i=[],r=0,o=t.getElementsByTagName(e);if("*"!==e)return o;for(;n=o[r++];)1===n.nodeType&&i.push(n);return i},_.find.CLASS=f.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&C)return t.getElementsByClassName(e)},s=[],g=[],(f.qsa=K.test(T.querySelectorAll))&&(le(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&g.push("[*^$]="+j+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||g.push("\\["+j+"*(?:value|"+L+")"),e.querySelectorAll("[id~="+D+"-]").length||g.push("~="),e.querySelectorAll(":checked").length||g.push(":checked"),e.querySelectorAll("a#"+D+"+*").length||g.push(".#.+[+~]")}),le(function(e){e.innerHTML="";var t=T.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&g.push("name"+j+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&g.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(f.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&le(function(e){f.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",R)}),g=g.length&&new RegExp(g.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,i=t&&t.parentNode;return e===i||!(!i||1!==i.nodeType||!(n.contains?n.contains(i):e.compareDocumentPosition&&16&e.compareDocumentPosition(i)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},S=t?function(e,t){if(e===t)return u=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!f.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument===y&&v(y,e)?-1:t===T||t.ownerDocument===y&&v(y,t)?1:l?P(l,e)-P(l,t):0:4&n?-1:1)}:function(e,t){if(e===t)return u=!0,0;var n,i=0,r=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!r||!o)return e===T?-1:t===T?1:r?-1:o?1:l?P(l,e)-P(l,t):0;if(r===o)return ce(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[i]===s[i];)i++;return i?ce(a[i],s[i]):a[i]===y?-1:s[i]===y?1:0}),T},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==T&&x(e),t=t.replace(W,"='$1']"),f.matchesSelector&&C&&!E[t+" "]&&(!s||!s.test(t))&&(!g||!g.test(t)))try{var n=c.call(e,t);if(n||f.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(X,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(X,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return z.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&U.test(n)&&(t=p(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(X,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=h[e+" "];return t||(t=new RegExp("(^|"+j+")"+e+"("+j+"|$)"))&&h(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,i,r){return function(e){var t=oe.attr(e,n);return null==t?"!="===i:!i||(t+="","="===i?t===r:"!="===i?t!==r:"^="===i?r&&0===t.indexOf(r):"*="===i?r&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function A(e,n,i){return y(n)?D.grep(e,function(e,t){return!!n.call(e,t,e)!==i}):n.nodeType?D.grep(e,function(e){return e===n!==i}):"string"!=typeof n?D.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(D.fn.init=function(e,t,n){var i,r;if(!e)return this;if(n=n||N,"string"!=typeof e)return e.nodeType?(this[0]=e,this.length=1,this):y(e)?void 0!==n.ready?n.ready(e):e(D):D.makeArray(e,this);if(!(i="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:O.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof D?t[0]:t,D.merge(this,D.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),M.test(i[1])&&D.isPlainObject(t))for(i in t)y(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(r=C.getElementById(i[2]))&&(this[0]=r,this.length=1),this}).prototype=D.fn,N=D(C);var I=/^(?:parents|prev(?:Until|All))/,P={children:!0,contents:!0,next:!0,prev:!0};function L(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}D.fn.extend({has:function(e){var t=D(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]+)/i,de=/^$|^module$|\/(?:java|ecma)script/i,he={option:[1,""],thead:[1,"
    ").addClass("cw").text("#"));t.isBefore(this._viewDate.clone().endOf("w"));)e.append(k("").addClass("dow").text(t.format("dd"))),t.add(1,"d");this.widget.find(".datepicker-days thead").append(e)},r.prototype._fillMonths=function(){for(var e=[],t=this._viewDate.clone().startOf("y").startOf("d");t.isSame(this._viewDate,"y");)e.push(k("").attr("data-action","selectMonth").addClass("month").text(t.format("MMM"))),t.add(1,"M");this.widget.find(".datepicker-months td").empty().append(e)},r.prototype._updateMonths=function(){var e=this.widget.find(".datepicker-months"),t=e.find("th"),n=e.find("tbody").find("span"),i=this;t.eq(0).find("span").attr("title",this._options.tooltips.prevYear),t.eq(1).attr("title",this._options.tooltips.selectYear),t.eq(2).find("span").attr("title",this._options.tooltips.nextYear),e.find(".disabled").removeClass("disabled"),this._isValid(this._viewDate.clone().subtract(1,"y"),"y")||t.eq(0).addClass("disabled"),t.eq(1).text(this._viewDate.year()),this._isValid(this._viewDate.clone().add(1,"y"),"y")||t.eq(2).addClass("disabled"),n.removeClass("active"),this._getLastPickedDate().isSame(this._viewDate,"y")&&!this.unset&&n.eq(this._getLastPickedDate().month()).addClass("active"),n.each(function(e){i._isValid(i._viewDate.clone().month(e),"M")||k(this).addClass("disabled")})},r.prototype._getStartEndYear=function(e,t){var n=e/10,i=Math.floor(t/e)*e;return[i,i+9*n,Math.floor(t/n)*n]},r.prototype._updateYears=function(){var e=this.widget.find(".datepicker-years"),t=e.find("th"),n=this._getStartEndYear(10,this._viewDate.year()),i=this._viewDate.clone().year(n[0]),r=this._viewDate.clone().year(n[1]),o="";for(t.eq(0).find("span").attr("title",this._options.tooltips.prevDecade),t.eq(1).attr("title",this._options.tooltips.selectDecade),t.eq(2).find("span").attr("title",this._options.tooltips.nextDecade),e.find(".disabled").removeClass("disabled"),this._options.minDate&&this._options.minDate.isAfter(i,"y")&&t.eq(0).addClass("disabled"),t.eq(1).text(i.year()+"-"+r.year()),this._options.maxDate&&this._options.maxDate.isBefore(r,"y")&&t.eq(2).addClass("disabled"),o+=''+(i.year()-1)+"";!i.isAfter(r,"y");)o+=''+i.year()+"",i.add(1,"y");o+=''+i.year()+"",e.find("td").html(o)},r.prototype._updateDecades=function(){var e=this.widget.find(".datepicker-decades"),t=e.find("th"),n=this._getStartEndYear(100,this._viewDate.year()),i=this._viewDate.clone().year(n[0]),r=this._viewDate.clone().year(n[1]),o=!1,a=!1,s=void 0,l="";for(t.eq(0).find("span").attr("title",this._options.tooltips.prevCentury),t.eq(2).find("span").attr("title",this._options.tooltips.nextCentury),e.find(".disabled").removeClass("disabled"),(0===i.year()||this._options.minDate&&this._options.minDate.isAfter(i,"y"))&&t.eq(0).addClass("disabled"),t.eq(1).text(i.year()+"-"+r.year()),this._options.maxDate&&this._options.maxDate.isBefore(r,"y")&&t.eq(2).addClass("disabled"),i.year()-10<0?l+=" ":l+=''+(i.year()-10)+"";!i.isAfter(r,"y");)s=i.year()+11,o=this._options.minDate&&this._options.minDate.isAfter(i,"y")&&this._options.minDate.year()<=s,a=this._options.maxDate&&this._options.maxDate.isAfter(i,"y")&&this._options.maxDate.year()<=s,l+=''+i.year()+"",i.add(10,"y");l+=''+i.year()+"",e.find("td").html(l)},r.prototype._fillDate=function(){var e=this.widget.find(".datepicker-days"),t=e.find("th"),n=[],i=void 0,r=void 0,o=void 0,a=void 0;if(this._hasDate()){for(t.eq(0).find("span").attr("title",this._options.tooltips.prevMonth),t.eq(1).attr("title",this._options.tooltips.selectMonth),t.eq(2).find("span").attr("title",this._options.tooltips.nextMonth),e.find(".disabled").removeClass("disabled"),t.eq(1).text(this._viewDate.format(this._options.dayViewHeaderFormat)),this._isValid(this._viewDate.clone().subtract(1,"M"),"M")||t.eq(0).addClass("disabled"),this._isValid(this._viewDate.clone().add(1,"M"),"M")||t.eq(2).addClass("disabled"),i=this._viewDate.clone().startOf("M").startOf("w").startOf("d"),a=0;a<42;a++){if(0===i.weekday()&&(r=k("
    '+i.week()+"'+i.date()+"
    '+t.format(this.use24Hours?"HH":"hh")+"
    '+t.format("mm")+"
    '+t.format("ss")+"
    ","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function fe(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?D.merge([e],n):n}function pe(e,t){for(var n=0,i=e.length;nx",v.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var be=C.documentElement,_e=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,xe=/^([^.]*)(?:\.(.+)|)/;function Te(){return!0}function Ce(){return!1}function De(){try{return C.activeElement}catch(e){}}function ke(e,t,n,i,r,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(i=i||n,n=void 0),t)ke(e,s,n,i,t[s],o);return e}if(null==i&&null==r?(r=n,i=n=void 0):null==r&&("string"==typeof n?(r=i,i=void 0):(r=i,i=n,n=void 0)),!1===r)r=Ce;else if(!r)return e;return 1===o&&(a=r,(r=function(e){return D().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=D.guid++)),e.each(function(){D.event.add(this,t,r,i,n)})}D.event={global:{},add:function(t,e,n,i,r){var o,a,s,l,u,c,d,h,f,p,m,g=K.get(t);if(g)for(n.handler&&(n=(o=n).handler,r=o.selector),r&&D.find.matchesSelector(be,r),n.guid||(n.guid=D.guid++),(l=g.events)||(l=g.events={}),(a=g.handle)||(a=g.handle=function(e){return void 0!==D&&D.event.triggered!==e.type?D.event.dispatch.apply(t,arguments):void 0}),u=(e=(e||"").match(j)||[""]).length;u--;)f=m=(s=xe.exec(e[u])||[])[1],p=(s[2]||"").split(".").sort(),f&&(d=D.event.special[f]||{},f=(r?d.delegateType:d.bindType)||f,d=D.event.special[f]||{},c=D.extend({type:f,origType:m,data:i,handler:n,guid:n.guid,selector:r,needsContext:r&&D.expr.match.needsContext.test(r),namespace:p.join(".")},o),(h=l[f])||((h=l[f]=[]).delegateCount=0,d.setup&&!1!==d.setup.call(t,i,p,a)||t.addEventListener&&t.addEventListener(f,a)),d.add&&(d.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),r?h.splice(h.delegateCount++,0,c):h.push(c),D.event.global[f]=!0)},remove:function(e,t,n,i,r){var o,a,s,l,u,c,d,h,f,p,m,g=K.hasData(e)&&K.get(e);if(g&&(l=g.events)){for(u=(t=(t||"").match(j)||[""]).length;u--;)if(f=m=(s=xe.exec(t[u])||[])[1],p=(s[2]||"").split(".").sort(),f){for(d=D.event.special[f]||{},h=l[f=(i?d.delegateType:d.bindType)||f]||[],s=s[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=h.length;o--;)c=h[o],!r&&m!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||i&&i!==c.selector&&("**"!==i||!c.selector)||(h.splice(o,1),c.selector&&h.delegateCount--,d.remove&&d.remove.call(e,c));a&&!h.length&&(d.teardown&&!1!==d.teardown.call(e,p,g.handle)||D.removeEvent(e,f,g.handle),delete l[f])}else for(f in l)D.event.remove(e,f+t[u],n,i,!0);D.isEmptyObject(l)&&K.remove(e,"handle events")}},dispatch:function(e){var t,n,i,r,o,a,s=D.event.fix(e),l=new Array(arguments.length),u=(K.get(this,"events")||{})[s.type]||[],c=D.event.special[s.type]||{};for(l[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,Se=/\s*$/g;function Ne(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&D(e).children("tbody")[0]||e}function Oe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Ie(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,i,r,o,a,s,l,u;if(1===t.nodeType){if(K.hasData(e)&&(o=K.access(e),a=K.set(t,o),u=o.events))for(r in delete a.handle,a.events={},u)for(n=0,i=u[r].length;n")},clone:function(e,t,n){var i,r,o,a,s,l,u,c=e.cloneNode(!0),d=D.contains(e.ownerDocument,e);if(!(v.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||D.isXMLDoc(e)))for(a=fe(c),i=0,r=(o=fe(e)).length;i").prop({charset:n.scriptCharset,src:n.url}).on("load error",r=function(e){i.remove(),r=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(i[0])},abort:function(){r&&r()}}});var Bt,Wt=[],Ut=/(=)\?(?=&|$)|\?\?/;D.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Wt.pop()||D.expando+"_"+wt++;return this[e]=!0,e}}),D.ajaxPrefilter("json jsonp",function(e,t,n){var i,r,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return i=e.jsonpCallback=y(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+i):!1!==e.jsonp&&(e.url+=(xt.test(e.url)?"&":"?")+e.jsonp+"="+i),e.converters["script json"]=function(){return o||D.error(i+" was not called"),o[0]},e.dataTypes[0]="json",r=T[i],T[i]=function(){o=arguments},n.always(function(){void 0===r?D(T).removeProp(i):T[i]=r,e[i]&&(e.jsonpCallback=t.jsonpCallback,Wt.push(i)),o&&y(r)&&r(o[0]),o=r=void 0}),"script"}),v.createHTMLDocument=((Bt=C.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Bt.childNodes.length),D.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((i=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(i)):t=C),o=!n&&[],(r=M.exec(e))?[t.createElement(r[1])]:(r=ye([e],t,o),o&&o.length&&D(o).remove(),D.merge([],r.childNodes)));var i,r,o},D.fn.load=function(e,t,n){var i,r,o,a=this,s=e.indexOf(" ");return-1").append(D.parseHTML(e)).find(i):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},D.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){D.fn[t]=function(e){return this.on(t,e)}}),D.expr.pseudos.animated=function(t){return D.grep(D.timers,function(e){return t===e.elem}).length},D.offset={setOffset:function(e,t,n){var i,r,o,a,s,l,u=D.css(e,"position"),c=D(e),d={};"static"===u&&(e.style.position="relative"),s=c.offset(),o=D.css(e,"top"),l=D.css(e,"left"),r=("absolute"===u||"fixed"===u)&&-1<(o+l).indexOf("auto")?(a=(i=c.position()).top,i.left):(a=parseFloat(o)||0,parseFloat(l)||0),y(t)&&(t=t.call(e,n,D.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+r),"using"in t?t.using.call(e,d):c.css(d)}},D.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){D.offset.setOffset(this,t,e)});var e,n,i=this[0];return i?i.getClientRects().length?(e=i.getBoundingClientRect(),n=i.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,i=this[0],r={top:0,left:0};if("fixed"===D.css(i,"position"))t=i.getBoundingClientRect();else{for(t=this.offset(),n=i.ownerDocument,e=i.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===D.css(e,"position");)e=e.parentNode;e&&e!==i&&1===e.nodeType&&((r=D(e).offset()).top+=D.css(e,"borderTopWidth",!0),r.left+=D.css(e,"borderLeftWidth",!0))}return{top:t.top-r.top-D.css(i,"marginTop",!0),left:t.left-r.left-D.css(i,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===D.css(e,"position");)e=e.offsetParent;return e||be})}}),D.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,r){var o="pageYOffset"===r;D.fn[t]=function(e){return B(this,function(e,t,n){var i;if(b(e)?i=e:9===e.nodeType&&(i=e.defaultView),void 0===n)return i?i[r]:e[t];i?i.scrollTo(o?i.pageXOffset:n,o?n:i.pageYOffset):e[t]=n},t,e,arguments.length)}}),D.each(["top","left"],function(e,n){D.cssHooks[n]=qe(v.pixelPosition,function(e,t){if(t)return t=Ye(e,n),He.test(t)?D(e).position()[n]+"px":t})}),D.each({Height:"height",Width:"width"},function(a,s){D.each({padding:"inner"+a,content:s,"":"outer"+a},function(i,o){D.fn[o]=function(e,t){var n=arguments.length&&(i||"boolean"!=typeof e),r=i||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var i;return b(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(i=e.documentElement,Math.max(e.body["scroll"+a],i["scroll"+a],e.body["offset"+a],i["offset"+a],i["client"+a])):void 0===n?D.css(e,t,r):D.style(e,t,n,r)},s,n?e:void 0,n)}})}),D.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){D.fn[n]=function(e,t){return 0").attr("name",i.submitButton.name).val(c(i.submitButton).val()).appendTo(i.currentForm)),!(i.settings.submitHandler&&!i.settings.debug)||(t=i.settings.submitHandler.call(i,i.currentForm,n),e&&e.remove(),void 0!==t&&t)}return i.settings.debug&&n.preventDefault(),i.cancelSubmit?(i.cancelSubmit=!1,e()):i.form()?i.pendingRequest?!(i.formSubmitted=!0):e():(i.focusInvalid(),!1)})),i)}e&&e.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing.")},valid:function(){var e,t,n;return c(this[0]).is("form")?e=this.validate().form():(n=[],e=!0,t=c(this[0].form).validate(),this.each(function(){(e=t.element(this)&&e)||(n=n.concat(t.errorList))}),t.errorList=n),e},rules:function(e,t){var n,i,r,o,a,s,l=this[0],u=void 0!==this.attr("contenteditable")&&"false"!==this.attr("contenteditable");if(null!=l&&(!l.form&&u&&(l.form=this.closest("form")[0],l.name=this.attr("name")),null!=l.form)){if(e)switch(i=(n=c.data(l.form,"validator").settings).rules,r=c.validator.staticRules(l),e){case"add":c.extend(r,c.validator.normalizeRule(t)),delete r.messages,i[l.name]=r,t.messages&&(n.messages[l.name]=c.extend(n.messages[l.name],t.messages));break;case"remove":return t?(s={},c.each(t.split(/\s/),function(e,t){s[t]=r[t],delete r[t]}),s):(delete i[l.name],r)}return(o=c.validator.normalizeRules(c.extend({},c.validator.classRules(l),c.validator.attributeRules(l),c.validator.dataRules(l),c.validator.staticRules(l)),l)).required&&(a=o.required,delete o.required,o=c.extend({required:a},o)),o.remote&&(a=o.remote,delete o.remote,o=c.extend(o,{remote:a})),o}}}),c.extend(c.expr.pseudos||c.expr[":"],{blank:function(e){return!c.trim(""+c(e).val())},filled:function(e){var t=c(e).val();return null!==t&&!!c.trim(""+t)},unchecked:function(e){return!c(e).prop("checked")}}),c.validator=function(e,t){this.settings=c.extend(!0,{},c.validator.defaults,e),this.currentForm=t,this.init()},c.validator.format=function(n,e){return 1===arguments.length?function(){var e=c.makeArray(arguments);return e.unshift(n),c.validator.format.apply(this,e)}:(void 0===e||(2Warning: No message defined for "+e.name+""),i=/\$?\{(\d+)\}/g;return"function"==typeof n?n=n.call(this,t.parameters,e):i.test(n)&&(n=c.validator.format(n.replace(i,"{$1}"),t.parameters)),n},formatAndAdd:function(e,t){var n=this.defaultMessage(e,t);this.errorList.push({message:n,element:e,method:t.method}),this.errorMap[e.name]=n,this.submitted[e.name]=n},addWrapper:function(e){return this.settings.wrapper&&(e=e.add(e.parent(this.settings.wrapper))),e},defaultShowErrors:function(){var e,t,n;for(e=0;this.errorList[e];e++)n=this.errorList[e],this.settings.highlight&&this.settings.highlight.call(this,n.element,this.settings.errorClass,this.settings.validClass),this.showLabel(n.element,n.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(e=0;this.successList[e];e++)this.showLabel(this.successList[e]);if(this.settings.unhighlight)for(e=0,t=this.validElements();t[e];e++)this.settings.unhighlight.call(this,t[e],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return c(this.errorList).map(function(){return this.element})},showLabel:function(e,t){var n,i,r,o,a=this.errorsFor(e),s=this.idOrName(e),l=c(e).attr("aria-describedby");a.length?(a.removeClass(this.settings.validClass).addClass(this.settings.errorClass),a.html(t)):(n=a=c("<"+this.settings.errorElement+">").attr("id",s+"-error").addClass(this.settings.errorClass).html(t||""),this.settings.wrapper&&(n=a.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(n):this.settings.errorPlacement?this.settings.errorPlacement.call(this,n,c(e)):n.insertAfter(e),a.is("label")?a.attr("for",s):0===a.parents("label[for='"+this.escapeCssMeta(s)+"']").length&&(r=a.attr("id"),l?l.match(new RegExp("\\b"+this.escapeCssMeta(r)+"\\b"))||(l+=" "+r):l=r,c(e).attr("aria-describedby",l),(i=this.groups[e.name])&&(o=this,c.each(o.groups,function(e,t){t===i&&c("[name='"+o.escapeCssMeta(e)+"']",o.currentForm).attr("aria-describedby",a.attr("id"))})))),!t&&this.settings.success&&(a.text(""),"string"==typeof this.settings.success?a.addClass(this.settings.success):this.settings.success(a,e)),this.toShow=this.toShow.add(a)},errorsFor:function(e){var t=this.escapeCssMeta(this.idOrName(e)),n=c(e).attr("aria-describedby"),i="label[for='"+t+"'], label[for='"+t+"'] *";return n&&(i=i+", #"+this.escapeCssMeta(n).replace(/\s+/g,", #")),this.errors().filter(i)},escapeCssMeta:function(e){return e.replace(/([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(e){return this.groups[e.name]||(this.checkable(e)?e.name:e.id||e.name)},validationTargetFor:function(e){return this.checkable(e)&&(e=this.findByName(e.name)),c(e).not(this.settings.ignore)[0]},checkable:function(e){return/radio|checkbox/i.test(e.type)},findByName:function(e){return c(this.currentForm).find("[name='"+this.escapeCssMeta(e)+"']")},getLength:function(e,t){switch(t.nodeName.toLowerCase()){case"select":return c("option:selected",t).length;case"input":if(this.checkable(t))return this.findByName(t.name).filter(":checked").length}return e.length},depend:function(e,t){return!this.dependTypes[typeof e]||this.dependTypes[typeof e](e,t)},dependTypes:{boolean:function(e){return e},string:function(e,t){return!!c(e,t.form).length},function:function(e,t){return e(t)}},optional:function(e){var t=this.elementValue(e);return!c.validator.methods.required.call(this,t,e)&&"dependency-mismatch"},startRequest:function(e){this.pending[e.name]||(this.pendingRequest++,c(e).addClass(this.settings.pendingClass),this.pending[e.name]=!0)},stopRequest:function(e,t){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[e.name],c(e).removeClass(this.settings.pendingClass),t&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(c(this.currentForm).submit(),this.submitButton&&c("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!t&&0===this.pendingRequest&&this.formSubmitted&&(c(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(e,t){return t="string"==typeof t&&t||"remote",c.data(e,"previousValue")||c.data(e,"previousValue",{old:null,valid:!0,message:this.defaultMessage(e,{method:t})})},destroy:function(){this.resetForm(),c(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur").find(".validate-lessThan-blur").off(".validate-lessThan").removeClass("validate-lessThan-blur").find(".validate-lessThanEqual-blur").off(".validate-lessThanEqual").removeClass("validate-lessThanEqual-blur").find(".validate-greaterThanEqual-blur").off(".validate-greaterThanEqual").removeClass("validate-greaterThanEqual-blur").find(".validate-greaterThan-blur").off(".validate-greaterThan").removeClass("validate-greaterThan-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(e,t){e.constructor===String?this.classRuleSettings[e]=t:c.extend(this.classRuleSettings,e)},classRules:function(e){var t={},n=c(e).attr("class");return n&&c.each(n.split(" "),function(){this in c.validator.classRuleSettings&&c.extend(t,c.validator.classRuleSettings[this])}),t},normalizeAttributeRule:function(e,t,n,i){/min|max|step/.test(n)&&(null===t||/number|range|text/.test(t))&&(i=Number(i),isNaN(i)&&(i=void 0)),i||0===i?e[n]=i:t===n&&"range"!==t&&(e[n]=!0)},attributeRules:function(e){var t,n,i={},r=c(e),o=e.getAttribute("type");for(t in c.validator.methods)n="required"===t?(""===(n=e.getAttribute(t))&&(n=!0),!!n):r.attr(t),this.normalizeAttributeRule(i,o,t,n);return i.maxlength&&/-1|2147483647|524288/.test(i.maxlength)&&delete i.maxlength,i},dataRules:function(e){var t,n,i={},r=c(e),o=e.getAttribute("type");for(t in c.validator.methods)""===(n=r.data("rule"+t.charAt(0).toUpperCase()+t.substring(1).toLowerCase()))&&(n=!0),this.normalizeAttributeRule(i,o,t,n);return i},staticRules:function(e){var t={},n=c.data(e.form,"validator");return n.settings.rules&&(t=c.validator.normalizeRule(n.settings.rules[e.name])||{}),t},normalizeRules:function(i,r){return c.each(i,function(e,t){if(!1!==t){if(t.param||t.depends){var n=!0;switch(typeof t.depends){case"string":n=!!c(t.depends,r.form).length;break;case"function":n=t.depends.call(r,r)}n?i[e]=void 0===t.param||t.param:(c.data(r.form,"validator").resetElements(c(r)),delete i[e])}}else delete i[e]}),c.each(i,function(e,t){i[e]=c.isFunction(t)&&"normalizer"!==e?t(r):t}),c.each(["minlength","maxlength"],function(){i[this]&&(i[this]=Number(i[this]))}),c.each(["rangelength","range"],function(){var e;i[this]&&(c.isArray(i[this])?i[this]=[Number(i[this][0]),Number(i[this][1])]:"string"==typeof i[this]&&(e=i[this].replace(/[\[\]]/g,"").split(/[\s,]+/),i[this]=[Number(e[0]),Number(e[1])]))}),c.validator.autoCreateRanges&&(null!=i.min&&null!=i.max&&(i.range=[i.min,i.max],delete i.min,delete i.max),null!=i.minlength&&null!=i.maxlength&&(i.rangelength=[i.minlength,i.maxlength],delete i.minlength,delete i.maxlength)),i},normalizeRule:function(e){if("string"==typeof e){var t={};c.each(e.split(/\s/),function(){t[this]=!0}),e=t}return e},addMethod:function(e,t,n){c.validator.methods[e]=t,c.validator.messages[e]=void 0!==n?n:c.validator.messages[e],t.length<3&&c.validator.addClassRules(e,c.validator.normalizeRule(e))},methods:{required:function(e,t,n){if(!this.depend(n,t))return"dependency-mismatch";if("select"!==t.nodeName.toLowerCase())return this.checkable(t)?0=n[0]&&i<=n[1]},min:function(e,t,n){return this.optional(t)||n<=e},max:function(e,t,n){return this.optional(t)||e<=n},range:function(e,t,n){return this.optional(t)||e>=n[0]&&e<=n[1]},step:function(e,t,n){var i,r=c(t).attr("type"),o="Step attribute on input type "+r+" is not supported.",a=new RegExp("\\b"+r+"\\b"),s=function(e){var t=(""+e).match(/(?:\.(\d+))?$/);return t&&t[1]?t[1].length:0},l=function(e){return Math.round(e*Math.pow(10,i))},u=!0;if(r&&!a.test(["text","number","range"].join()))throw new Error(o);return i=s(n),(s(e)>i||l(e)%l(n)!=0)&&(u=!1),this.optional(t)||u},equalTo:function(e,t,n){var i=c(n);return this.settings.onfocusout&&i.not(".validate-equalTo-blur").length&&i.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){c(t).valid()}),e===i.val()},remote:function(o,a,e,s){if(this.optional(a))return"dependency-mismatch";s="string"==typeof s&&s||"remote";var l,t,n,u=this.previousValue(a,s);return this.settings.messages[a.name]||(this.settings.messages[a.name]={}),u.originalMessage=u.originalMessage||this.settings.messages[a.name][s],this.settings.messages[a.name][s]=u.message,e="string"==typeof e&&{url:e}||e,n=c.param(c.extend({data:o},e.data)),u.old===n?u.valid:(u.old=n,(l=this).startRequest(a),(t={})[a.name]=o,c.ajax(c.extend(!0,{mode:"abort",port:"validate"+a.name,dataType:"json",data:t,context:l.currentForm,success:function(e){var t,n,i,r=!0===e||"true"===e;l.settings.messages[a.name][s]=u.originalMessage,r?(i=l.formSubmitted,l.resetInternals(),l.toHide=l.errorsFor(a),l.formSubmitted=i,l.successList.push(a),l.invalid[a.name]=!1,l.showErrors()):(t={},n=e||l.defaultMessage(a,{method:s,parameters:o}),t[a.name]=u.message=n,l.invalid[a.name]=!0,l.showErrors(t)),u.valid=r,l.stopRequest(a,r)}},e)),"pending")}}});var i,r={};return c.ajaxPrefilter?c.ajaxPrefilter(function(e,t,n){var i=e.port;"abort"===e.mode&&(r[i]&&r[i].abort(),r[i]=n)}):(i=c.ajax,c.ajax=function(e){var t=("mode"in e?e:c.ajaxSettings).mode,n=("port"in e?e:c.ajaxSettings).port;return"abort"===t?(r[n]&&r[n].abort(),r[n]=i.apply(this,arguments),r[n]):i.apply(this,arguments)}),c}),function(e){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery-validation"],e):"object"==typeof module&&module.exports?module.exports=e(require("jquery-validation")):jQuery.validator.unobtrusive=e(jQuery)}(function(l){var e,a=l.validator,s="unobtrusiveValidation";function u(e,t,n){e.rules[t]=n,e.message&&(e.messages[t]=e.message)}function c(e){return e.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function d(e){return e.substr(0,e.lastIndexOf(".")+1)}function h(e,t){return 0===e.indexOf("*.")&&(e=e.replace("*.",t)),e}function f(e){var t=l(this),n="__jquery_unobtrusive_validation_form_reset";if(!t.data(n)){t.data(n,!0);try{t.data("validator").resetForm()}finally{t.removeData(n)}t.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),t.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function p(i){var e=l(i),t=e.data(s),n=l.proxy(f,i),r=a.unobtrusive.options||{},o=function(e,t){var n=r[e];n&&l.isFunction(n)&&n.apply(i,t)};return t||(t={options:{errorClass:r.errorClass||"input-validation-error",errorElement:r.errorElement||"span",errorPlacement:function(){(function(e,t){var n=l(this).find("[data-valmsg-for='"+c(t[0].name)+"']"),i=n.attr("data-valmsg-replace"),r=i?!1!==l.parseJSON(i):null;n.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",n),r?(n.empty(),e.removeClass("input-validation-error").appendTo(n)):e.hide()}).apply(i,arguments),o("errorPlacement",arguments)},invalidHandler:function(){(function(e,t){var n=l(this).find("[data-valmsg-summary=true]"),i=n.find("ul");i&&i.length&&t.errorList.length&&(i.empty(),n.addClass("validation-summary-errors").removeClass("validation-summary-valid"),l.each(t.errorList,function(){l("
  • ").html(this.message).appendTo(i)}))}).apply(i,arguments),o("invalidHandler",arguments)},messages:{},rules:{},success:function(){(function(e){var t=e.data("unobtrusiveContainer");if(t){var n=t.attr("data-valmsg-replace"),i=n?l.parseJSON(n):null;t.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),i&&t.empty()}}).apply(i,arguments),o("success",arguments)}},attachValidation:function(){e.off("reset."+s,n).on("reset."+s,n).validate(this.options)},validate:function(){return e.validate(),e.valid()}},e.data(s,t)),t}return a.unobtrusive={adapters:[],parseElement:function(i,e){var t,r,o,a=l(i),s=a.parents("form")[0];s&&((t=p(s)).options.rules[i.name]=r={},t.options.messages[i.name]=o={},l.each(this.adapters,function(){var e="data-val-"+this.name,t=a.attr(e),n={};void 0!==t&&(e+="-",l.each(this.params,function(){n[this]=a.attr(e+this)}),this.adapt({element:i,form:s,message:t,params:n,rules:r,messages:o}))}),l.extend(r,{__dummy__:!0}),e||t.attachValidation())},parse:function(e){var t=l(e),n=t.parents().addBack().filter("form").add(t.find("form")).has("[data-val=true]");t.find("[data-val=true]").each(function(){a.unobtrusive.parseElement(this,!0)}),n.each(function(){var e=p(this);e&&e.attachValidation()})}},(e=a.unobtrusive.adapters).add=function(e,t,n){return n||(n=t,t=[]),this.push({name:e,params:t,adapt:n}),this},e.addBool=function(t,n){return this.add(t,function(e){u(e,n||t,!0)})},e.addMinMax=function(e,i,r,o,t,n){return this.add(e,[t||"min",n||"max"],function(e){var t=e.params.min,n=e.params.max;t&&n?u(e,o,[t,n]):t?u(e,i,t):n&&u(e,r,n)})},e.addSingleVal=function(t,n,i){return this.add(t,[n||"val"],function(e){u(e,i||t,e.params[n])})},a.addMethod("__dummy__",function(e,t,n){return!0}),a.addMethod("regex",function(e,t,n){var i;return!!this.optional(t)||(i=new RegExp(n).exec(e))&&0===i.index&&i[0].length===e.length}),a.addMethod("nonalphamin",function(e,t,n){var i;return n&&(i=(i=e.match(/\W/g))&&i.length>=n),i}),a.methods.extension?(e.addSingleVal("accept","mimtype"),e.addSingleVal("extension","extension")):e.addSingleVal("extension","extension","accept"),e.addSingleVal("regex","pattern"),e.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),e.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),e.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),e.add("equalto",["other"],function(e){var t=d(e.element.name),n=h(e.params.other,t);u(e,"equalTo",l(e.form).find(":input").filter("[name='"+c(n)+"']")[0])}),e.add("required",function(e){"INPUT"===e.element.tagName.toUpperCase()&&"CHECKBOX"===e.element.type.toUpperCase()||u(e,"required",!0)}),e.add("remote",["url","type","additionalfields"],function(i){var r={url:i.params.url,type:i.params.type||"GET",data:{}},o=d(i.element.name);l.each((i.params.additionalfields||i.element.name).replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g),function(e,t){var n=h(t,o);r.data[n]=function(){var e=l(i.form).find(":input").filter("[name='"+c(n)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),u(i,"remote",r)}),e.add("password",["min","nonalphamin","regex"],function(e){e.params.min&&u(e,"minlength",e.params.min),e.params.nonalphamin&&u(e,"nonalphamin",e.params.nonalphamin),e.params.regex&&u(e,"regex",e.params.regex)}),e.add("fileextensions",["extensions"],function(e){u(e,"extension",e.params.extensions)}),l(function(){a.unobtrusive.parse(document)}),a.unobtrusive}),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Popper=t()}(this,function(){"use strict";for(var e="undefined"!=typeof window&&"undefined"!=typeof document,t=["Edge","Trident","Firefox"],n=0,i=0;i=i.clientWidth&&n>=i.clientHeight}),c=0l[e]&&!i.escapeWithReference&&(n=Math.min(c[t],l[e]-("right"===e?c.width:c.height))),w({},t,n)}};return u.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";c=x({},c,d[t](e))}),e.offsets.popper=c,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,r=e.placement.split("-")[0],o=Math.floor,a=-1!==["top","bottom"].indexOf(r),s=a?"right":"bottom",l=a?"left":"top",u=a?"width":"height";return n[s]o(i[s])&&(e.offsets.popper[l]=o(i[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!q(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],o=e.offsets,a=o.popper,s=o.reference,l=-1!==["left","right"].indexOf(r),u=l?"height":"width",c=l?"Top":"Left",d=c.toLowerCase(),h=l?"left":"top",f=l?"bottom":"right",p=M(i)[u];s[f]-pa[f]&&(e.offsets.popper[d]+=s[d]+p-a[f]),e.offsets.popper=T(e.offsets.popper);var m=s[d]+s[u]/2-p/2,g=_(e.instance.popper),v=parseFloat(g["margin"+c],10),y=parseFloat(g["border"+c+"Width"],10),b=m-e.offsets.popper[d]-v-y;return b=Math.max(Math.min(a[u]-p,b),0),e.arrowElement=i,e.offsets.arrow=(w(n={},d,Math.round(b)),w(n,h,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,m){if(P(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var g=h(p.instance.popper,p.instance.reference,m.padding,m.boundariesElement,p.positionFixed),v=p.placement.split("-")[0],y=A(v),b=p.placement.split("-")[1]||"",_=[];switch(m.behavior){case U:_=[v,y];break;case $:_=W(v);break;case z:_=W(v,!0);break;default:_=m.behavior}return _.forEach(function(e,t){if(v!==e||_.length===t+1)return p;v=p.placement.split("-")[0],y=A(v);var n,i=p.offsets.popper,r=p.offsets.reference,o=Math.floor,a="left"===v&&o(i.right)>o(r.left)||"right"===v&&o(i.left)o(r.top)||"bottom"===v&&o(i.top)o(g.right),u=o(i.top)o(g.bottom),d="left"===v&&s||"right"===v&&l||"top"===v&&u||"bottom"===v&&c,h=-1!==["top","bottom"].indexOf(v),f=!!m.flipVariations&&(h&&"start"===b&&s||h&&"end"===b&&l||!h&&"start"===b&&u||!h&&"end"===b&&c);(a||d||f)&&(p.flipped=!0,(a||d)&&(v=_[t+1]),f&&(b="end"===(n=b)?"start":"start"===n?"end":n),p.placement=v+(b?"-"+b:""),p.offsets.popper=x({},p.offsets.popper,N(p.instance.popper,p.offsets.reference,p.placement)),p=I(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,r=i.popper,o=i.reference,a=-1!==["left","right"].indexOf(n),s=-1===["top","left"].indexOf(n);return r[a?"left":"top"]=o[n]-(s?r[a?"width":"height"]:0),e.placement=A(t),e.offsets.popper=T(r),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=O(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightthis._items.length-1||e<0))if(this._isSliding)O(this._element).one(W.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
    ',trigger:"hover focus",title:"",delay:0,html:!(Dt={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Ct={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},St="out",Mt={HIDE:"hide"+_t,HIDDEN:"hidden"+_t,SHOW:(Et="show")+_t,SHOWN:"shown"+_t,INSERTED:"inserted"+_t,CLICK:"click"+_t,FOCUSIN:"focusin"+_t,FOCUSOUT:"focusout"+_t,MOUSEENTER:"mouseenter"+_t,MOUSELEAVE:"mouseleave"+_t},At="fade",Nt="show",Ot=".tooltip-inner",It=".arrow",Pt="hover",Lt="focus",jt="click",Ht="manual",Ft=function(){function i(e,t){if(void 0===c)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=vt(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),vt(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(vt(this.getTipElement()).hasClass(Nt))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),vt.removeData(this.element,this.constructor.DATA_KEY),vt(this.element).off(this.constructor.EVENT_KEY),vt(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&vt(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===vt(this.element).css("display"))throw new Error("Please use show on visible elements");var e=vt.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){vt(this.element).trigger(e);var n=vt.contains(this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=qn.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&vt(i).addClass(At);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,a=this._getAttachment(o);this.addAttachmentClass(a);var s=!1===this.config.container?document.body:vt(document).find(this.config.container);vt(i).data(this.constructor.DATA_KEY,this),vt.contains(this.element.ownerDocument.documentElement,this.tip)||vt(i).appendTo(s),vt(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new c(this.element,i,{placement:a,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:It},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){t._handlePopperPlacementChange(e)}}),vt(i).addClass(Nt),"ontouchstart"in document.documentElement&&vt(document.body).children().on("mouseover",null,vt.noop);var l=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,vt(t.element).trigger(t.constructor.Event.SHOWN),e===St&&t._leave(null,t)};if(vt(this.tip).hasClass(At)){var u=qn.getTransitionDurationFromElement(this.tip);vt(this.tip).one(qn.TRANSITION_END,l).emulateTransitionEnd(u)}else l()}},e.hide=function(e){var t=this,n=this.getTipElement(),i=vt.Event(this.constructor.Event.HIDE),r=function(){t._hoverState!==Et&&n.parentNode&&n.parentNode.removeChild(n),t._cleanTipClass(),t.element.removeAttribute("aria-describedby"),vt(t.element).trigger(t.constructor.Event.HIDDEN),null!==t._popper&&t._popper.destroy(),e&&e()};if(vt(this.element).trigger(i),!i.isDefaultPrevented()){if(vt(n).removeClass(Nt),"ontouchstart"in document.documentElement&&vt(document.body).children().off("mouseover",null,vt.noop),this._activeTrigger[jt]=!1,this._activeTrigger[Lt]=!1,this._activeTrigger[Pt]=!1,vt(this.tip).hasClass(At)){var o=qn.getTransitionDurationFromElement(n);vt(n).one(qn.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){vt(this.getTipElement()).addClass(xt+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||vt(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(vt(e.querySelectorAll(Ot)),this.getTitle()),vt(e).removeClass(At+" "+Nt)},e.setElementContent=function(e,t){var n=this.config.html;"object"==typeof t&&(t.nodeType||t.jquery)?n?vt(t).parent().is(e)||e.empty().append(t):e.text(vt(t).text()):e[n?"html":"text"](t)},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e||(e="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),e},e._getAttachment=function(e){return Dt[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)vt(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==Ht){var t=e===Pt?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Pt?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;vt(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}vt(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==e)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||vt(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),vt(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Lt:Pt]=!0),vt(t.getTipElement()).hasClass(Nt)||t._hoverState===Et?t._hoverState=Et:(clearTimeout(t._timeout),t._hoverState=Et,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===Et&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||vt(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),vt(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Lt:Pt]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=St,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===St&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){return"number"==typeof(e=l({},this.constructor.Default,vt(this.element).data(),"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),qn.typeCheckConfig(yt,e,this.constructor.DefaultType),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=vt(this.getTipElement()),t=e.attr("class").match(Tt);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(vt(e).removeClass(At),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=vt(this).data(bt),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),vt(this).data(bt,e)),"string"==typeof n)){if(void 0===e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},a(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return kt}},{key:"NAME",get:function(){return yt}},{key:"DATA_KEY",get:function(){return bt}},{key:"Event",get:function(){return Mt}},{key:"EVENT_KEY",get:function(){return _t}},{key:"DefaultType",get:function(){return Ct}}]),i}(),vt.fn[yt]=Ft._jQueryInterface,vt.fn[yt].Constructor=Ft,vt.fn[yt].noConflict=function(){return vt.fn[yt]=wt,Ft._jQueryInterface},Ft),Jn=(Yt="popover",Vt="."+(qt="bs.popover"),Bt=(Rt=t).fn[Yt],Wt="bs-popover",Ut=new RegExp("(^|\\s)"+Wt+"\\S+","g"),$t=l({},Gn.Default,{placement:"right",trigger:"click",content:"",template:''}),zt=l({},Gn.DefaultType,{content:"(string|element|function)"}),Gt="fade",Kt=".popover-header",Qt=".popover-body",Zt={HIDE:"hide"+Vt,HIDDEN:"hidden"+Vt,SHOW:(Jt="show")+Vt,SHOWN:"shown"+Vt,INSERTED:"inserted"+Vt,CLICK:"click"+Vt,FOCUSIN:"focusin"+Vt,FOCUSOUT:"focusout"+Vt,MOUSEENTER:"mouseenter"+Vt,MOUSELEAVE:"mouseleave"+Vt},Xt=function(e){var t,n;function i(){return e.apply(this,arguments)||this}n=e,(t=i).prototype=Object.create(n.prototype),(t.prototype.constructor=t).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(e){Rt(this.getTipElement()).addClass(Wt+"-"+e)},r.getTipElement=function(){return this.tip=this.tip||Rt(this.config.template)[0],this.tip},r.setContent=function(){var e=Rt(this.getTipElement());this.setElementContent(e.find(Kt),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find(Qt),t),e.removeClass(Gt+" "+Jt)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var e=Rt(this.getTipElement()),t=e.attr("class").match(Ut);null!==t&&0=this._offsets[r]&&(void 0===this._offsets[r+1]||e>>0;if("function"!=typeof e)throw TypeError();var i,r=arguments[1];for(i=0;i>16&255)),n.push(String.fromCharCode(i>>8&255)),n.push(String.fromCharCode(255&i)),i=r=0),t+=1;return 12===r?(i>>=4,n.push(String.fromCharCode(255&i))):18===r&&(i>>=2,n.push(String.fromCharCode(i>>8&255)),n.push(String.fromCharCode(255&i))),n.join("")},e.btoa=e.btoa||function(e){e=String(e);var t,n,i,r,o,a,s,l=0,u=[];if(/[^\x00-\xFF]/.test(e))throw Error("InvalidCharacterError");for(;l>2,o=(3&t)<<4|(n=e.charCodeAt(l++))>>4,a=(15&n)<<2|(i=e.charCodeAt(l++))>>6,s=63&i,l===e.length+2?s=a=64:l===e.length+1&&(s=64),u.push(c.charAt(r),c.charAt(o),c.charAt(a),c.charAt(s));return u.join("")},Object.prototype.hasOwnProperty||(Object.prototype.hasOwnProperty=function(e){var t=this.__proto__||this.constructor.prototype;return e in this&&(!(e in t)||t[e]!==this[e])}),function(){if("performance"in r==!1&&(r.performance={}),Date.now=Date.now||function(){return(new Date).getTime()},"now"in r.performance==!1){var e=Date.now();performance.timing&&performance.timing.navigationStart&&(e=performance.timing.navigationStart),r.performance.now=function(){return Date.now()-e}}}(),r.requestAnimationFrame||(r.webkitRequestAnimationFrame&&r.webkitCancelAnimationFrame?((i=r).requestAnimationFrame=function(e){return webkitRequestAnimationFrame(function(){e(i.performance.now())})},i.cancelAnimationFrame=i.webkitCancelAnimationFrame):r.mozRequestAnimationFrame&&r.mozCancelAnimationFrame?((n=r).requestAnimationFrame=function(e){return mozRequestAnimationFrame(function(){e(n.performance.now())})},n.cancelAnimationFrame=n.mozCancelAnimationFrame):((t=r).requestAnimationFrame=function(e){return t.setTimeout(e,1e3/60)},t.cancelAnimationFrame=t.clearTimeout))}}(this),function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Holder=t():e.Holder=t()}(this,function(){return function(n){var i={};function r(e){if(i[e])return i[e].exports;var t=i[e]={exports:{},id:e,loaded:!1};return n[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}return r.m=n,r.c=i,r.p="",r(0)}([function(e,t,n){e.exports=n(1)},function(s,e,E){(function(u){var e=E(2),h=E(3),P=E(6),m=E(7),g=E(8),v=E(9),L=E(10),t=E(11),c=E(12),d=E(15),y=m.extend,b=m.dimensionCheck,_=t.svg_ns,i={version:t.version,addTheme:function(e,t){return null!=e&&null!=t&&(j.settings.themes[e]=t),delete j.vars.cache.themeKeys,this},addImage:function(i,e){return v.getNodeArray(e).forEach(function(e){var t=v.newEl("img"),n={};n[j.setup.dataAttr]=i,v.setAttr(t,n),e.appendChild(t)}),this},setResizeUpdate:function(e,t){e.holderData&&(e.holderData.resizeUpdate=!!t,e.holderData.resizeUpdate&&x(e))},run:function(e){e=e||{};var c={},d=y(j.settings,e);j.vars.preempted=!0,j.vars.dataAttr=d.dataAttr||j.setup.dataAttr,c.renderer=d.renderer?d.renderer:j.setup.renderer,-1===j.setup.renderers.join(",").indexOf(c.renderer)&&(c.renderer=j.setup.supportsSVG?"svg":j.setup.supportsCanvas?"canvas":"html");var t=v.getNodeArray(d.images),n=v.getNodeArray(d.bgnodes),i=v.getNodeArray(d.stylenodes),r=v.getNodeArray(d.objects);return c.stylesheets=[],c.svgXMLStylesheet=!0,c.noFontFallback=!!d.noFontFallback,c.noBackgroundSize=!!d.noBackgroundSize,i.forEach(function(e){if(e.attributes.rel&&e.attributes.href&&"stylesheet"==e.attributes.rel.value){var t=e.attributes.href.value,n=v.newEl("a");n.href=t;var i=n.protocol+"//"+n.host+n.pathname+n.search;c.stylesheets.push(i)}}),n.forEach(function(e){if(u.getComputedStyle){var t=u.getComputedStyle(e,null).getPropertyValue("background-image"),n=e.getAttribute("data-background-src")||t,i=null,r=d.domain+"/",o=n.indexOf(r);if(0===o)i=n;else if(1===o&&"?"===n[0])i=n.slice(1);else{var a=n.substr(o).match(/([^\"]*)"?\)/);if(null!==a)i=a[1];else if(0===n.indexOf("url("))throw"Holder: unable to parse background URL: "+n}if(i){var s=l(i,d);s&&p({mode:"background",el:e,flags:s,engineSettings:c})}}}),r.forEach(function(e){var t={};try{t.data=e.getAttribute("data"),t.dataSrc=e.getAttribute(j.vars.dataAttr)}catch(e){}var n=null!=t.data&&0===t.data.indexOf(d.domain),i=null!=t.dataSrc&&0===t.dataSrc.indexOf(d.domain);n?f(d,c,t.data,e):i&&f(d,c,t.dataSrc,e)}),t.forEach(function(e){var t={};try{t.src=e.getAttribute("src"),t.dataSrc=e.getAttribute(j.vars.dataAttr),t.rendered=e.getAttribute("data-holder-rendered")}catch(e){}var n,i,r,o,a,s=null!=t.src,l=null!=t.dataSrc&&0===t.dataSrc.indexOf(d.domain),u=null!=t.rendered&&"true"==t.rendered;s?0===t.src.indexOf(d.domain)?f(d,c,t.src,e):l&&(u?f(d,c,t.dataSrc,e):(n=t.src,i=d,r=c,o=t.dataSrc,a=e,m.imageExists(n,function(e){e||f(i,r,o,a)}))):l&&f(d,c,t.dataSrc,e)}),this}},j={settings:{domain:"holder.js",images:"img",objects:"object",bgnodes:"body .holderjs",stylenodes:"head link.holderjs",themes:{gray:{bg:"#EEEEEE",fg:"#AAAAAA"},social:{bg:"#3a5a97",fg:"#FFFFFF"},industrial:{bg:"#434A52",fg:"#C2F200"},sky:{bg:"#0D8FDB",fg:"#FFFFFF"},vine:{bg:"#39DBAC",fg:"#1E292C"},lava:{bg:"#F8591A",fg:"#1C2846"}}},defaults:{size:10,units:"pt",scale:1/16}};function f(e,t,n,i){var r=l(n.substr(n.lastIndexOf(e.domain)),e);r&&p({mode:null,el:i,flags:r,engineSettings:t})}function l(e,t){var n={theme:y(j.settings.themes.gray,null),stylesheets:t.stylesheets,instanceOptions:t},i=e.indexOf("?"),r=[e];-1!==i&&(r=[e.slice(0,i),e.slice(i+1)]);var o=r[0].split("/");n.holderURL=e;var a=o[1],s=a.match(/([\d]+p?)x([\d]+p?)/);if(!s)return!1;if(n.fluid=-1!==a.indexOf("p"),n.dimensions={width:s[1].replace("p","%"),height:s[2].replace("p","%")},2===r.length){var l=h.parse(r[1]);if(m.truthy(l.ratio)){n.fluid=!0;var u=parseFloat(n.dimensions.width.replace("%","")),c=parseFloat(n.dimensions.height.replace("%",""));c=Math.floor(c/u*100),u=100,n.dimensions.width=u+"%",n.dimensions.height=c+"%"}if(n.auto=m.truthy(l.auto),l.bg&&(n.theme.bg=m.parseColor(l.bg)),l.fg&&(n.theme.fg=m.parseColor(l.fg)),l.bg&&!l.fg&&(n.autoFg=!0),l.theme&&n.instanceOptions.themes.hasOwnProperty(l.theme)&&(n.theme=y(n.instanceOptions.themes[l.theme],null)),l.text&&(n.text=l.text),l.textmode&&(n.textmode=l.textmode),l.size&&(n.size=l.size),l.font&&(n.font=l.font),l.align&&(n.align=l.align),l.lineWrap&&(n.lineWrap=l.lineWrap),n.nowrap=m.truthy(l.nowrap),n.outline=m.truthy(l.outline),m.truthy(l.random)){j.vars.cache.themeKeys=j.vars.cache.themeKeys||Object.keys(n.instanceOptions.themes);var d=j.vars.cache.themeKeys[0|Math.random()*j.vars.cache.themeKeys.length];n.theme=y(n.instanceOptions.themes[d],null)}}return n}function p(e){var t=e.mode,n=e.el,i=e.flags,r=e.engineSettings,o=i.dimensions,a=i.theme,s=o.width+"x"+o.height;t=null==t?i.fluid?"fluid":"image":t;if(null!=i.text&&(a.text=i.text,"object"===n.nodeName.toLowerCase())){for(var l=a.text.split("\\n"),u=0;u=f||!0===I)&&(k(x,D,S,x.properties.leading),x.add(D),S=0,M+=x.properties.leading,A+=1,(D=new m.Group("line"+A)).y=M),!0!==I&&(C.moveTo(S,0),S+=T.spaceWidth+O.width,D.add(C))}if(k(x,D,S,x.properties.leading),x.add(D),"left"===e.align)x.moveTo(e.width-h,null,null);else if("right"===e.align){for(E in x.children)(D=x.children[E]).moveTo(e.width-D.width,null,null);x.moveTo(0-(e.width-h),null,null)}else{for(E in x.children)(D=x.children[E]).moveTo((x.width-D.width)/2,null,null);x.moveTo((e.width-x.width)/2,null,null)}x.moveTo(null,(e.height-x.height)/2,null),(e.height-x.height)/2<0&&x.moveTo(null,0,null)}else C=new m.Text(e.text),(D=new m.Group("line0")).add(C),x.add(D),"left"===e.align?x.moveTo(e.width-h,null,null):"right"===e.align?x.moveTo(0-(e.width-h),null,null):x.moveTo((e.width-T.boundingBox.width)/2,null,null),x.moveTo(null,(e.height-T.boundingBox.height)/2,null);return p}(a);function l(){var e=null;switch(o.renderer){case"canvas":e=d(s,t);break;case"svg":e=c(s,t);break;default:throw"Holder: invalid renderer: "+o.renderer}return e}if(null==(e=l()))throw"Holder: couldn't render placeholder";"background"==n?(i.style.backgroundImage="url("+e+")",o.noBackgroundSize||(i.style.backgroundSize=a.width+"px "+a.height+"px")):("img"===i.nodeName.toLowerCase()?v.setAttr(i,{src:e}):"object"===i.nodeName.toLowerCase()&&v.setAttr(i,{data:e,type:"image/svg+xml"}),o.reRender&&u.setTimeout(function(){var e=l();if(null==e)throw"Holder: couldn't render placeholder";"img"===i.nodeName.toLowerCase()?v.setAttr(i,{src:e}):"object"===i.nodeName.toLowerCase()&&v.setAttr(i,{data:e,type:"image/svg+xml"})},150)),v.setAttr(i,{"data-holder-rendered":!0})}function x(e){for(var t,n=0,i=(t=null==e||null==e.nodeType?j.vars.resizableImages:[e]).length;n","application/xml")},t.getNodeArray=function(e){var t=null;return"string"==typeof e?t=document.querySelectorAll(e):n.NodeList&&e instanceof n.NodeList?t=e:n.Node&&e instanceof n.Node?t=[e]:n.HTMLCollection&&e instanceof n.HTMLCollection?t=e:e instanceof Array?t=e:null===e&&(t=[]),t=Array.prototype.slice.call(t)}}).call(t,function(){return this}())},function(e,t){var a=function(e,t){"string"==typeof e&&("#"===(this.original=e).charAt(0)&&(e=e.slice(1)),/[^a-f0-9]+/i.test(e)||(3===e.length&&(e=e.replace(/./g,"$&$&")),6===e.length&&(this.alpha=1,t&&t.alpha&&(this.alpha=t.alpha),this.set(parseInt(e,16)))))};a.rgb2hex=function(e,t,n){return[e,t,n].map(function(e){var t=(0|e).toString(16);return e<16&&(t="0"+t),t}).join("")},a.hsl2rgb=function(e,t,n){var i=e/60,r=(1-Math.abs(2*n-1))*t,o=r*(1-Math.abs(parseInt(i)%2-1)),a=n-r/2,s=0,l=0,u=0;return 0<=i&&i<1?(s=r,l=o):1<=i&&i<2?(s=o,l=r):2<=i&&i<3?(l=r,u=o):3<=i&&i<4?(l=o,u=r):4<=i&&i<5?(s=o,u=r):5<=i&&i<6&&(s=r,u=o),s+=a,l+=a,u+=a,[s=parseInt(255*s),l=parseInt(255*l),u=parseInt(255*u)]},a.prototype.set=function(e){this.raw=e;var t=(16711680&this.raw)>>16,n=(65280&this.raw)>>8,i=255&this.raw,r=.2126*t+.7152*n+.0722*i,o=-.09991*t-.33609*n+.436*i,a=.615*t-.55861*n-.05639*i;return this.rgb={r:t,g:n,b:i},this.yuv={y:r,u:o,v:a},this},a.prototype.lighten=function(e){var t=255*(Math.min(1,Math.max(0,Math.abs(e)))*(e<0?-1:1))|0,n=Math.min(255,Math.max(0,this.rgb.r+t)),i=Math.min(255,Math.max(0,this.rgb.g+t)),r=Math.min(255,Math.max(0,this.rgb.b+t)),o=a.rgb2hex(n,i,r);return new a(o)},a.prototype.toHex=function(e){return(e?"#":"")+this.raw.toString(16)},a.prototype.lighterThan=function(e){return e instanceof a||(e=new a(e)),this.yuv.y>e.yuv.y},a.prototype.blendAlpha=function(e){e instanceof a||(e=new a(e));var t=e,n=t.alpha*t.rgb.r+(1-t.alpha)*this.rgb.r,i=t.alpha*t.rgb.g+(1-t.alpha)*this.rgb.g,r=t.alpha*t.rgb.b+(1-t.alpha)*this.rgb.b;return new a(a.rgb2hex(n,i,r))},e.exports=a},function(e,t){e.exports={version:"2.9.4",svg_ns:"http://www.w3.org/2000/svg"}},function(e,t,n){var C=n(13),D=n(8),i=n(11),k=n(7),E=i.svg_ns,S=function(e){var t=e.tag,n=e.content||"";return delete e.tag,delete e.content,[t,n,e]};e.exports=function(e,t){var n,i=t.engineSettings.stylesheets.map(function(e){return''}).join("\n"),r="holder_"+Number(new Date).toString(16),o=e.root,a=o.children.holderTextGroup,s="#"+r+" text { "+(n=a.properties,k.cssProps({fill:n.fill,"font-weight":n.font.weight,"font-family":n.font.family+", monospace","font-size":n.font.size+n.font.units}))+" } ";a.y+=.8*a.textPositionData.boundingBox.height;var l=[];Object.keys(a.children).forEach(function(e){var o=a.children[e];Object.keys(o.children).forEach(function(e){var t=o.children[e],n=a.x+o.x+t.x,i=a.y+o.y+t.y,r=S({tag:"text",content:t.properties.text,x:n,y:i});l.push(r)})});var u,c,d,h,f=S({tag:"g",content:l}),p=null;if(o.children.holderBg.properties.outline){var m=o.children.holderBg.properties.outline;p=S({tag:"path",d:(u=o.children.holderBg.width,c=o.children.holderBg.height,d=m.width,h=d/2,["M",h,h,"H",u-h,"V",c-h,"H",h,"V",0,"M",0,h,"L",u,c-h,"M",0,c-h,"L",u,h].join(" ")),"stroke-width":m.width,stroke:m.fill,fill:"none"})}var g,v=(g=o.children.holderBg,S({tag:"rect",width:g.width,height:g.height,fill:g.properties.fill})),y=[];y.push(v),m&&y.push(p),y.push(f);var b=S({tag:"g",id:r,content:y}),_=S({tag:"style",content:s,type:"text/css"}),w=S({tag:"defs",content:_}),x=S({tag:"svg",content:[w,b],width:o.properties.width,height:o.properties.height,xmlns:E,viewBox:[0,0,o.properties.width,o.properties.height].join(" "),preserveAspectRatio:"none"}),T=C(x);return T=i+T[0],D.svgStringToDataURI(T,"background"===t.mode)}},function(e,t,n){n(14);e.exports=function e(t,n,i){"use strict";var r,o,a,s,l,u,c,d,h,f,p,m,g=1,v=!0;function y(e,t){if(null!==t&&!1!==t&&void 0!==t)return"string"!=typeof t&&"object"!=typeof t?String(t):t}if(i=i||{},"string"==typeof t[0])t[0]=(l=t[0],u=l.match(/^[\w-]+/),c={tag:u?u[0]:"div",attr:{},children:[]},d=l.match(/#([\w-]+)/),h=l.match(/\$([\w-]+)/),f=l.match(/\.[\w-]+/g),d&&(c.attr.id=d[1],i[d[1]]=c),h&&(i[h[1]]=c),f&&(c.attr.class=f.join(" ").replace(/\./g,"")),l.match(/&$/g)&&(v=!1),c);else{if(!Array.isArray(t[0]))throw new Error("First element of array must be a string, or an array and not "+JSON.stringify(t[0]));g=0}for(;g/g,">"))),t[0].children.push(t[g]);else if("number"==typeof t[g])t[0].children.push(t[g]);else if(Array.isArray(t[g])){if(Array.isArray(t[g][0])){if(t[g].reverse().forEach(function(e){t.splice(g+1,0,e)}),0!==g)continue;g++}e(t[g],n,i),t[g][0]&&t[0].children.push(t[g][0])}else if("function"==typeof t[g])a=t[g];else{if("object"!=typeof t[g])throw new TypeError('"'+t[g]+'" is not allowed as a value.');for(o in t[g])t[g].hasOwnProperty(o)&&null!==t[g][o]&&!1!==t[g][o]&&("style"===o&&"object"==typeof t[g][o]?t[0].attr[o]=JSON.stringify(t[g][o],y).slice(2,-2).replace(/","/g,";").replace(/":"/g,":").replace(/\\"/g,"'"):t[0].attr[o]=t[g][o])}}if(!1!==t[0]){for(s in r="<"+t[0].tag,t[0].attr)t[0].attr.hasOwnProperty(s)&&(r+=" "+s+'="'+((m=t[0].attr[s])||0===m?String(m).replace(/&/g,"&").replace(/"/g,"""):"")+'"');r+=">",t[0].children.forEach(function(e){r+=e}),r+="",t[0]=r}return i[0]=t[0],a&&a(t[0]),i}},function(e,t){"use strict";var s=/["'&<>]/;e.exports=function(e){var t,n=""+e,i=s.exec(n);if(!i)return n;var r="",o=0,a=0;for(o=i.index;oe.length)&&e.substring(0,t.length)===t},Rc:function(e,t){if(e===t)return!0;if(11===e.nodeType)return!1;if(t.contains)return t.contains(3===e.nodeType?e.parentNode:e);if(t.compareDocumentPosition)return 16==(16&t.compareDocumentPosition(e));for(;e&&e!=t;)e=e.parentNode;return!!e},qb:function(e){return D.a.Rc(e,e.ownerDocument.documentElement)},Tb:function(e){return!!D.a.Vb(e,D.a.qb)},A:function(e){return e&&e.tagName&&e.tagName.toLowerCase()},Zb:function(e){return D.onError?function(){try{return e.apply(this,arguments)}catch(e){throw D.onError&&D.onError(e),e}}:e},setTimeout:function(e,t){return setTimeout(D.a.Zb(e),t)},dc:function(e){setTimeout(function(){throw D.onError&&D.onError(e),e},0)},q:function(t,e,n){var i=D.a.Zb(n);if(n=l&&s[e],D.options.useOnlyNativeEvents||n||!Nla)if(n||"function"!=typeof t.addEventListener){if(void 0===t.attachEvent)throw Error("Browser doesn't support addEventListener or attachEvent");var r=function(e){i.call(t,e)},o="on"+e;t.attachEvent(o,r),D.a.G.qa(t,function(){t.detachEvent(o,r)})}else t.addEventListener(e,i,!1);else Nla(t).bind(e,i)},Fa:function(e,t){if(!e||!e.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var n;if(n=!("input"!==D.a.A(e)||!e.type||"click"!=t.toLowerCase()||"checkbox"!=(n=e.type)&&"radio"!=n),D.options.useOnlyNativeEvents||!Nla||n)if("function"==typeof Lla.createEvent){if("function"!=typeof e.dispatchEvent)throw Error("The supplied element doesn't support dispatchEvent");(n=Lla.createEvent(a[t]||"HTMLEvents")).initEvent(t,!0,!0,Kla,0,0,0,0,0,!1,!1,!1,!1,0,e),e.dispatchEvent(n)}else if(n&&e.click)e.click();else{if(void 0===e.fireEvent)throw Error("Browser doesn't support triggering events");e.fireEvent("on"+t)}else Nla(e).trigger(t)},c:function(e){return D.I(e)?e():e},Bb:function(e){return D.I(e)?e.p():e},fb:function(t,e,n){var i;e&&("object"==typeof t.classList?(i=t.classList[n?"add":"remove"],D.a.r(e.match(u),function(e){i.call(t.classList,e)})):"string"==typeof t.className.baseVal?r(t.className,"baseVal",e,n):r(t,"className",e,n))},bb:function(e,t){var n=D.a.c(t);null!==n&&n!==Jla||(n="");var i=D.f.firstChild(e);!i||3!=i.nodeType||D.f.nextSibling(i)?D.f.fa(e,[e.ownerDocument.createTextNode(n)]):i.data=n,D.a.Wc(e)},vc:function(e,t){if(e.name=t,l<=7)try{e.mergeAttributes(Lla.createElement(""),!1)}catch(e){}},Wc:function(e){9<=l&&(e=1==e.nodeType?e:e.parentNode).style&&(e.style.zoom=e.style.zoom)},Sc:function(e){if(l){var t=e.style.width;e.style.width=0,e.style.width=t}},nd:function(e,t){e=D.a.c(e),t=D.a.c(t);for(var n=[],i=e;i<=t;i++)n.push(i);return n},W:function(e){for(var t=[],n=0,i=e.length;n","
  • "],tbody:c,tfoot:c,tr:[2,"","
    "],td:d=[3,"","
    "],th:d,option:h=[1,""],optgroup:h},p=D.a.C<=8,D.a.na=function(e,t){var n;if(Nla){if(Nla.parseHTML)n=Nla.parseHTML(e,t)||[];else if((n=Nla.clean([e],t))&&n[0]){for(var i=n[0];i.parentNode&&11!==i.parentNode.nodeType;)i=i.parentNode;i.parentNode&&i.parentNode.removeChild(i)}}else{(n=t)||(n=Lla),i=n.parentWindow||n.defaultView||Kla;var r,o=D.a.cb(e).toLowerCase(),a=n.createElement("div");for(o=(r=(o=o.match(/^<([a-z]+)[ >]/))&&f[o[1]]||u)[0],r="ignored
    "+r[1]+e+r[2]+"
    ","function"==typeof i.innerShiv?a.appendChild(i.innerShiv(r)):(p&&n.appendChild(a),a.innerHTML=r,p&&a.parentNode.removeChild(a));o--;)a=a.lastChild;n=D.a.W(a.lastChild.childNodes)}return n},D.a.Eb=function(e,t){if(D.a.rb(e),null!==(t=D.a.c(t))&&t!==Jla)if("string"!=typeof t&&(t=t.toString()),Nla)Nla(e).html(t);else for(var n=D.a.na(t,e.ownerDocument),i=0;i]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,n=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Tc:function(e,t,n){t.isTemplateRewritten(e,n)||t.rewriteTemplate(e,function(e){return D.Ib.jd(e,t)},n)},jd:function(e,o){return e.replace(t,function(e,t,n,i,r){return a(r,t,n,o)}).replace(n,function(e,t){return a(t,"\x3c!-- ko --\x3e","#comment",o)})},Jc:function(i,r){return D.N.yb(function(e,t){var n=e.nextSibling;n&&n.nodeName.toLowerCase()===r&&D.La(n,i,t)})}}}(),D.b("__tr_ambtns",D.Ib.Jc),function(){D.v={},D.v.n=function(e){if(this.n=e){var t=D.a.A(e);this.eb="script"===t?1:"textarea"===t?2:"template"==t&&e.content&&11===e.content.nodeType?3:4}},D.v.n.prototype.text=function(){var e=1===this.eb?"text":2===this.eb?"value":"innerHTML";if(0==arguments.length)return this.n[e];var t=arguments[0];"innerHTML"===e?D.a.Eb(this.n,t):this.n[e]=t};var t=D.a.e.J()+"_";D.v.n.prototype.data=function(e){if(1===arguments.length)return D.a.e.get(this.n,t+e);D.a.e.set(this.n,t+e,arguments[1])};var n=D.a.e.J();D.v.n.prototype.nodes=function(){var e=this.n;if(0==arguments.length)return(D.a.e.get(e,n)||{}).mb||(3===this.eb?e.content:4===this.eb?e:Jla);D.a.e.set(e,n,{mb:arguments[0]})},D.v.sa=function(e){this.n=e},D.v.sa.prototype=new D.v.n,D.v.sa.prototype.text=function(){if(0==arguments.length){var e=D.a.e.get(this.n,n)||{};return e.Jb===Jla&&e.mb&&(e.Jb=e.mb.innerHTML),e.Jb}D.a.e.set(this.n,n,{Jb:arguments[0]})},D.b("templateSources",D.v),D.b("templateSources.domElement",D.v.n),D.b("templateSources.anonymousTemplate",D.v.sa)}(),function(){function i(e,t,n){var i;for(t=D.f.nextSibling(t);e&&(i=e)!==t;)n(i,e=D.f.nextSibling(i))}function u(e,t){if(e.length){var r=e[0],o=e[e.length-1],n=r.parentNode,a=D.S.instance,s=a.preprocessNode;if(s){if(i(r,o,function(e,t){var n=e.previousSibling,i=s.call(a,e);i&&(e===r&&(r=i[0]||t),e===o&&(o=i[i.length-1]||n))}),e.length=0,!r)return;r===o?e.push(r):(e.push(r,o),D.a.Ba(e,n))}i(r,o,function(e){1!==e.nodeType&&8!==e.nodeType||D.Ub(t,e)}),i(r,o,function(e){1!==e.nodeType&&8!==e.nodeType||D.N.Cc(e,[t])}),D.a.Ba(e,n)}}function l(e){return e.nodeType?e:0"+t+"<\/script>")},0").attr("id",n.containerId).addClass(n.positionClass)).appendTo(T(n.target)),g=g),g;var n}function s(e,t,n){var i=!(!n||!n.force)&&n.force;return!(!e||!i&&0!==T(":focus",e).length||(e[t.hideMethod]({duration:t.hideDuration,easing:t.hideEasing,complete:function(){x(e)}}),0))}function _(e){t&&t(e)}function l(t){var r=w(),e=t.iconClass||r.iconClass;if(void 0!==t.optionsOverride&&(r=T.extend(r,t.optionsOverride),e=t.optionsOverride.iconClass||e),!function(e,t){if(e.preventDuplicates){if(t.message===v)return!0;v=t.message}return!1}(r,t)){y++,g=b(r,!0);var o=null,a=T("
    "),n=T("
    "),i=T("
    "),s=T("
    "),l=T(r.closeHtml),u={intervalId:null,hideEta:null,maxHideTime:null},c={toastId:y,state:"visible",startTime:new Date,options:r,map:t};return t.iconClass&&a.addClass(r.toastClass).addClass(e),function(){if(t.title){var e=t.title;r.escapeHtml&&(e=d(t.title)),n.append(e).addClass(r.titleClass),a.append(n)}}(),function(){if(t.message){var e=t.message;r.escapeHtml&&(e=d(t.message)),i.append(e).addClass(r.messageClass),a.append(i)}}(),r.closeButton&&(l.addClass(r.closeClass).attr("role","button"),a.prepend(l)),r.progressBar&&(s.addClass(r.progressClass),a.prepend(s)),r.rtl&&a.addClass("rtl"),r.newestOnTop?g.prepend(a):g.append(a),function(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}a.attr("aria-live",e)}(),a.hide(),a[r.showMethod]({duration:r.showDuration,easing:r.showEasing,complete:r.onShown}),0/g,">")}function h(e){var t=e&&!1!==r.closeMethod?r.closeMethod:r.hideMethod,n=e&&!1!==r.closeDuration?r.closeDuration:r.hideDuration,i=e&&!1!==r.closeEasing?r.closeEasing:r.hideEasing;if(!T(":focus",a).length||e)return clearTimeout(u.intervalId),a[t]({duration:n,easing:i,complete:function(){x(a),clearTimeout(o),r.onHidden&&"hidden"!==c.state&&r.onHidden(),c.state="hidden",c.endTime=new Date,_(c)}})}function f(){(0×',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1},e.options)}function x(e){g||(g=b()),e.is(":visible")||(e.remove(),e=null,0===g.children().length&&(g.remove(),v=void 0))}}()}),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,r;function y(){return e.apply(null,arguments)}function s(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function l(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function u(e){return void 0===e}function c(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function d(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function h(e,t){var n,i=[];for(n=0;n>>0,i=0;ixe(e)?(o=e+1,s-xe(e)):(o=e,s),{year:o,dayOfYear:a}}function qe(e,t,n){var i,r,o=Re(e.year(),t,n),a=Math.floor((e.dayOfYear()-o-1)/7)+1;return a<1?i=a+Ve(r=e.year()-1,t,n):a>Ve(e.year(),t,n)?(i=a-Ve(e.year(),t,n),r=e.year()+1):(r=e.year(),i=a),{week:i,year:r}}function Ve(e,t,n){var i=Re(e,t,n),r=Re(e+1,t,n);return(xe(e)-i+r)/7}V("w",["ww",2],"wo","week"),V("W",["WW",2],"Wo","isoWeek"),O("week","w"),O("isoWeek","W"),j("week",5),j("isoWeek",5),le("w",K),le("ww",K,$),le("W",K),le("WW",K,$),he(["w","ww","W","WW"],function(e,t,n,i){t[i.substr(0,1)]=C(e)}),V("d",0,"do","day"),V("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),V("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),V("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),V("e",0,0,"weekday"),V("E",0,0,"isoWeekday"),O("day","d"),O("weekday","e"),O("isoWeekday","E"),j("day",11),j("weekday",11),j("isoWeekday",11),le("d",K),le("e",K),le("E",K),le("dd",function(e,t){return t.weekdaysMinRegex(e)}),le("ddd",function(e,t){return t.weekdaysShortRegex(e)}),le("dddd",function(e,t){return t.weekdaysRegex(e)}),he(["dd","ddd","dddd"],function(e,t,n,i){var r=n._locale.weekdaysParse(e,i,n._strict);null!=r?t.d=r:_(n).invalidWeekday=e}),he(["d","e","E"],function(e,t,n,i){t[i]=C(e)});var Be="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),We="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Ue="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),$e=ae,ze=ae,Ge=ae;function Je(){function e(e,t){return t.length-e.length}var t,n,i,r,o,a=[],s=[],l=[],u=[];for(t=0;t<7;t++)n=p([2e3,1]).day(t),i=this.weekdaysMin(n,""),r=this.weekdaysShort(n,""),o=this.weekdays(n,""),a.push(i),s.push(r),l.push(o),u.push(i),u.push(r),u.push(o);for(a.sort(e),s.sort(e),l.sort(e),u.sort(e),t=0;t<7;t++)s[t]=ue(s[t]),l[t]=ue(l[t]),u[t]=ue(u[t]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Ke(){return this.hours()%12||12}function Qe(e,t){V(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function Ze(e,t){return t._meridiemParse}V("H",["HH",2],0,"hour"),V("h",["hh",2],0,Ke),V("k",["kk",2],0,function(){return this.hours()||24}),V("hmm",0,0,function(){return""+Ke.apply(this)+H(this.minutes(),2)}),V("hmmss",0,0,function(){return""+Ke.apply(this)+H(this.minutes(),2)+H(this.seconds(),2)}),V("Hmm",0,0,function(){return""+this.hours()+H(this.minutes(),2)}),V("Hmmss",0,0,function(){return""+this.hours()+H(this.minutes(),2)+H(this.seconds(),2)}),Qe("a",!0),Qe("A",!1),O("hour","h"),j("hour",13),le("a",Ze),le("A",Ze),le("H",K),le("h",K),le("k",K),le("HH",K,$),le("hh",K,$),le("kk",K,$),le("hmm",Q),le("hmmss",Z),le("Hmm",Q),le("Hmmss",Z),de(["H","HH"],ge),de(["k","kk"],function(e,t,n){var i=C(e);t[ge]=24===i?0:i}),de(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),de(["h","hh"],function(e,t,n){t[ge]=C(e),_(n).bigHour=!0}),de("hmm",function(e,t,n){var i=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i)),_(n).bigHour=!0}),de("hmmss",function(e,t,n){var i=e.length-4,r=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i,2)),t[ye]=C(e.substr(r)),_(n).bigHour=!0}),de("Hmm",function(e,t,n){var i=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i))}),de("Hmmss",function(e,t,n){var i=e.length-4,r=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i,2)),t[ye]=C(e.substr(r))});var Xe,et=ke("Hours",!0),tt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ne,monthsShort:Oe,week:{dow:0,doy:6},weekdays:Be,weekdaysMin:Ue,weekdaysShort:We,meridiemParse:/[ap]\.?m?\.?/i},nt={},it={};function rt(e){return e?e.toLowerCase().replace("_","-"):e}function ot(e){var t=null;if(!nt[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=Xe._abbr,require("./locale/"+e),at(t)}catch(e){}return nt[e]}function at(e,t){var n;return e&&((n=u(t)?lt(e):st(e,t))?Xe=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),Xe._abbr}function st(e,t){if(null===t)return delete nt[e],null;var n,i=tt;if(t.abbr=e,null!=nt[e])E("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),i=nt[e]._config;else if(null!=t.parentLocale)if(null!=nt[t.parentLocale])i=nt[t.parentLocale]._config;else{if(null==(n=ot(t.parentLocale)))return it[t.parentLocale]||(it[t.parentLocale]=[]),it[t.parentLocale].push({name:e,config:t}),null;i=n._config}return nt[e]=new A(M(i,t)),it[e]&&it[e].forEach(function(e){st(e.name,e.config)}),at(e),nt[e]}function lt(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return Xe;if(!s(e)){if(t=ot(e))return t;e=[e]}return function(e){for(var t,n,i,r,o=0;o=t&&a(r,n,!0)>=t-1)break;t--}o++}return Xe}(e)}function ut(e){var t,n=e._a;return n&&-2===_(e).overflow&&(t=n[pe]<0||11Me(n[fe],n[pe])?me:n[ge]<0||24Ve(n,o,a)?_(e)._overflowWeeks=!0:null!=l?_(e)._overflowWeekday=!0:(s=Ye(n,i,r,o,a),e._a[fe]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(o=ct(e._a[fe],i[fe]),(e._dayOfYear>xe(o)||0===e._dayOfYear)&&(_(e)._overflowDayOfYear=!0),n=Fe(o,0,e._dayOfYear),e._a[pe]=n.getUTCMonth(),e._a[me]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=i[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[ve]&&0===e._a[ye]&&0===e._a[be]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Fe:function(e,t,n,i,r,o,a){var s=new Date(e,t,n,i,r,o,a);return e<100&&0<=e&&isFinite(s.getFullYear())&&s.setFullYear(e),s}).apply(null,a),r=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(_(e).weekdayMismatch=!0)}}var ht=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ft=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,pt=/Z|[+-]\d\d(?::?\d\d)?/,mt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],gt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],vt=/^\/?Date\((\-?\d+)/i;function yt(e){var t,n,i,r,o,a,s=e._i,l=ht.exec(s)||ft.exec(s);if(l){for(_(e).iso=!0,t=0,n=mt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},sn.isLocal=function(){return!!this.isValid()&&!this._isUTC},sn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},sn.isUtc=Ft,sn.isUTC=Ft,sn.zoneAbbr=function(){return this._isUTC?"UTC":""},sn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},sn.dates=n("dates accessor is deprecated. Use date instead.",en),sn.months=n("months accessor is deprecated. Use month instead",Pe),sn.years=n("years accessor is deprecated. Use year instead",De),sn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),sn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!u(this._isDSTShifted))return this._isDSTShifted;var e={};if(v(e,this),(e=Tt(e))._a){var t=e._isUTC?p(e._a):Dt(e._a);this._isDSTShifted=this.isValid()&&0 div").hide().filter(".datepicker-"+d[this.currentViewMode].CLASS_NAME).show())},o.prototype._isInDisabledDates=function(e){return!0===this._options.disabledDates[e.format("YYYY-MM-DD")]},o.prototype._isInEnabledDates=function(e){return!0===this._options.enabledDates[e.format("YYYY-MM-DD")]},o.prototype._isInDisabledHours=function(e){return!0===this._options.disabledHours[e.format("H")]},o.prototype._isInEnabledHours=function(e){return!0===this._options.enabledHours[e.format("H")]},o.prototype._isValid=function(e,t){if(!e.isValid())return!1;if(this._options.disabledDates&&"d"===t&&this._isInDisabledDates(e))return!1;if(this._options.enabledDates&&"d"===t&&!this._isInEnabledDates(e))return!1;if(this._options.minDate&&e.isBefore(this._options.minDate,t))return!1;if(this._options.maxDate&&e.isAfter(this._options.maxDate,t))return!1;if(this._options.daysOfWeekDisabled&&"d"===t&&-1!==this._options.daysOfWeekDisabled.indexOf(e.day()))return!1;if(this._options.disabledHours&&("h"===t||"m"===t||"s"===t)&&this._isInDisabledHours(e))return!1;if(this._options.enabledHours&&("h"===t||"m"===t||"s"===t)&&!this._isInEnabledHours(e))return!1;if(this._options.disabledTimeIntervals&&("h"===t||"m"===t||"s"===t)){var n=!1;if(r.each(this._options.disabledTimeIntervals,function(){if(e.isBetween(this[0],this[1]))return!(n=!0)}),n)return!1}return!0},o.prototype._parseInputDate=function(e){return void 0===this._options.parseInputDate?n.isMoment(e)||(e=this.getMoment(e)):e=this._options.parseInputDate(e),e},o.prototype._keydown=function(e){var t=null,n=void 0,i=void 0,r=void 0,o=void 0,a=[],s={},l=e.which;for(n in p[l]="p",p)p.hasOwnProperty(n)&&"p"===p[n]&&(a.push(n),parseInt(n,10)!==l&&(s[n]=!0));for(n in this._options.keyBinds)if(this._options.keyBinds.hasOwnProperty(n)&&"function"==typeof this._options.keyBinds[n]&&(r=n.split(" ")).length===a.length&&h[l]===r[r.length-1]){for(o=!0,i=r.length-2;0<=i;i--)if(!(h[r[i]]in s)){o=!1;break}if(o){t=this._options.keyBinds[n];break}}t&&t.call(this)&&(e.stopPropagation(),e.preventDefault())},o.prototype._keyup=function(e){p[e.which]="r",m[e.which]&&(m[e.which]=!1,e.stopPropagation(),e.preventDefault())},o.prototype._indexGivenDates=function(e){var t={},n=this;return r.each(e,function(){var e=n._parseInputDate(this);e.isValid()&&(t[e.format("YYYY-MM-DD")]=!0)}),!!Object.keys(t).length&&t},o.prototype._indexGivenHours=function(e){var t={};return r.each(e,function(){t[this]=!0}),!!Object.keys(t).length&&t},o.prototype._initFormatting=function(){var e=this._options.format||"L LT",t=this;this.actualFormat=e.replace(/(\[[^\[]*])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(e){return t._dates[0].localeData().longDateFormat(e)||e}),this.parseFormats=this._options.extraFormats?this._options.extraFormats.slice():[],this.parseFormats.indexOf(e)<0&&this.parseFormats.indexOf(this.actualFormat)<0&&this.parseFormats.push(this.actualFormat),this.use24Hours=this.actualFormat.toLowerCase().indexOf("a")<1&&this.actualFormat.replace(/\[.*?]/g,"").indexOf("h")<1,this._isEnabled("y")&&(this.MinViewModeNumber=2),this._isEnabled("M")&&(this.MinViewModeNumber=1),this._isEnabled("d")&&(this.MinViewModeNumber=0),this.currentViewMode=Math.max(this.MinViewModeNumber,this.currentViewMode),this.unset||this._setValue(this._dates[0],0)},o.prototype._getLastPickedDate=function(){return this._dates[this._getLastPickedDateIndex()]},o.prototype._getLastPickedDateIndex=function(){return this._dates.length-1},o.prototype.getMoment=function(e){var t=void 0;return t=null==e?n():this._hasTimeZone()?n.tz(e,this.parseFormats,this._options.locale,this._options.useStrict,this._options.timeZone):n(e,this.parseFormats,this._options.locale,this._options.useStrict),this._hasTimeZone()&&t.tz(this._options.timeZone),t},o.prototype.toggle=function(){return this.widget?this.hide():this.show()},o.prototype.ignoreReadonly=function(e){if(0===arguments.length)return this._options.ignoreReadonly;if("boolean"!=typeof e)throw new TypeError("ignoreReadonly () expects a boolean parameter");this._options.ignoreReadonly=e},o.prototype.options=function(e){if(0===arguments.length)return r.extend(!0,{},this._options);if(!(e instanceof Object))throw new TypeError("options() this.options parameter should be an object");r.extend(!0,this._options,e);var n=this;r.each(this._options,function(e,t){void 0!==n[e]&&n[e](t)})},o.prototype.date=function(e,t){if(t=t||0,0===arguments.length)return this.unset?null:this._options.allowMultidate?this._dates.join(this._options.multidateSeparator):this._dates[t].clone();if(!(null===e||"string"==typeof e||n.isMoment(e)||e instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");this._setValue(null===e?null:this._parseInputDate(e),t)},o.prototype.format=function(e){if(0===arguments.length)return this._options.format;if("string"!=typeof e&&("boolean"!=typeof e||!1!==e))throw new TypeError("format() expects a string or boolean:false parameter "+e);this._options.format=e,this.actualFormat&&this._initFormatting()},o.prototype.timeZone=function(e){if(0===arguments.length)return this._options.timeZone;if("string"!=typeof e)throw new TypeError("newZone() expects a string parameter");this._options.timeZone=e},o.prototype.dayViewHeaderFormat=function(e){if(0===arguments.length)return this._options.dayViewHeaderFormat;if("string"!=typeof e)throw new TypeError("dayViewHeaderFormat() expects a string parameter");this._options.dayViewHeaderFormat=e},o.prototype.extraFormats=function(e){if(0===arguments.length)return this._options.extraFormats;if(!1!==e&&!(e instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");this._options.extraFormats=e,this.parseFormats&&this._initFormatting()},o.prototype.disabledDates=function(e){if(0===arguments.length)return this._options.disabledDates?r.extend({},this._options.disabledDates):this._options.disabledDates;if(!e)return this._options.disabledDates=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("disabledDates() expects an array parameter");this._options.disabledDates=this._indexGivenDates(e),this._options.enabledDates=!1,this._update()},o.prototype.enabledDates=function(e){if(0===arguments.length)return this._options.enabledDates?r.extend({},this._options.enabledDates):this._options.enabledDates;if(!e)return this._options.enabledDates=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("enabledDates() expects an array parameter");this._options.enabledDates=this._indexGivenDates(e),this._options.disabledDates=!1,this._update()},o.prototype.daysOfWeekDisabled=function(e){if(0===arguments.length)return this._options.daysOfWeekDisabled.splice(0);if("boolean"==typeof e&&!e)return this._options.daysOfWeekDisabled=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");if(this._options.daysOfWeekDisabled=e.reduce(function(e,t){return 6<(t=parseInt(t,10))||t<0||isNaN(t)||-1===e.indexOf(t)&&e.push(t),e},[]).sort(),this._options.useCurrent&&!this._options.keepInvalid)for(var t=0;t").append(k("
    ").addClass("prev").attr("data-action","previous").append(k("").addClass(this._options.icons.previous))).append(k("").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",this._options.calendarWeeks?"6":"5")).append(k("").addClass("next").attr("data-action","next").append(k("").addClass(this._options.icons.next)))),t=k("
    ").attr("colspan",this._options.calendarWeeks?"8":"7")));return[k("
    ").addClass("datepicker-days").append(k("").addClass("table table-sm").append(e).append(k(""))),k("
    ").addClass("datepicker-months").append(k("
    ").addClass("table-condensed").append(e.clone()).append(t.clone())),k("
    ").addClass("datepicker-years").append(k("
    ").addClass("table-condensed").append(e.clone()).append(t.clone())),k("
    ").addClass("datepicker-decades").append(k("
    ").addClass("table-condensed").append(e.clone()).append(t.clone()))]},r.prototype._getTimePickerMainTemplate=function(){var e=k(""),t=k(""),n=k("");return this._isEnabled("h")&&(e.append(k("
    ").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementHour}).addClass("btn").attr("data-action","incrementHours").append(k("").addClass(this._options.icons.up)))),t.append(k("").append(k("").addClass("timepicker-hour").attr({"data-time-component":"hours",title:this._options.tooltips.pickHour}).attr("data-action","showHours"))),n.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementHour}).addClass("btn").attr("data-action","decrementHours").append(k("").addClass(this._options.icons.down))))),this._isEnabled("m")&&(this._isEnabled("h")&&(e.append(k("").addClass("separator")),t.append(k("").addClass("separator").html(":")),n.append(k("").addClass("separator"))),e.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementMinute}).addClass("btn").attr("data-action","incrementMinutes").append(k("").addClass(this._options.icons.up)))),t.append(k("").append(k("").addClass("timepicker-minute").attr({"data-time-component":"minutes",title:this._options.tooltips.pickMinute}).attr("data-action","showMinutes"))),n.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementMinute}).addClass("btn").attr("data-action","decrementMinutes").append(k("").addClass(this._options.icons.down))))),this._isEnabled("s")&&(this._isEnabled("m")&&(e.append(k("").addClass("separator")),t.append(k("").addClass("separator").html(":")),n.append(k("").addClass("separator"))),e.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementSecond}).addClass("btn").attr("data-action","incrementSeconds").append(k("").addClass(this._options.icons.up)))),t.append(k("").append(k("").addClass("timepicker-second").attr({"data-time-component":"seconds",title:this._options.tooltips.pickSecond}).attr("data-action","showSeconds"))),n.append(k("").append(k("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementSecond}).addClass("btn").attr("data-action","decrementSeconds").append(k("").addClass(this._options.icons.down))))),this.use24Hours||(e.append(k("").addClass("separator")),t.append(k("").append(k("").addClass("separator"))),k("
    ").addClass("timepicker-picker").append(k("").addClass("table-condensed").append([e,t,n]))},r.prototype._getTimePickerTemplate=function(){var e=k("
    ").addClass("timepicker-hours").append(k("
    ").addClass("table-condensed")),t=k("
    ").addClass("timepicker-minutes").append(k("
    ").addClass("table-condensed")),n=k("
    ").addClass("timepicker-seconds").append(k("
    ").addClass("table-condensed")),i=[this._getTimePickerMainTemplate()];return this._isEnabled("h")&&i.push(e),this._isEnabled("m")&&i.push(t),this._isEnabled("s")&&i.push(n),i},r.prototype._getToolbar=function(){var e=[];if(this._options.buttons.showToday&&e.push(k("
    ").append(k("").attr({href:"#",tabindex:"-1","data-action":"today",title:this._options.tooltips.today}).append(k("").addClass(this._options.icons.today)))),!this._options.sideBySide&&this._hasDate()&&this._hasTime()){var t=void 0,n=void 0;n="times"===this._options.viewMode?(t=this._options.tooltips.selectDate,this._options.icons.date):(t=this._options.tooltips.selectTime,this._options.icons.time),e.push(k("").append(k("").attr({href:"#",tabindex:"-1","data-action":"togglePicker",title:t}).append(k("").addClass(n))))}return this._options.buttons.showClear&&e.push(k("").append(k("").attr({href:"#",tabindex:"-1","data-action":"clear",title:this._options.tooltips.clear}).append(k("").addClass(this._options.icons.clear)))),this._options.buttons.showClose&&e.push(k("").append(k("").attr({href:"#",tabindex:"-1","data-action":"close",title:this._options.tooltips.close}).append(k("").addClass(this._options.icons.close)))),0===e.length?"":k("").addClass("table-condensed").append(k("").append(k("").append(e)))},r.prototype._getTemplate=function(){var e=k("
    ").addClass("bootstrap-datetimepicker-widget dropdown-menu"),t=k("
    ").addClass("datepicker").append(this._getDatePickerTemplate()),n=k("
    ").addClass("timepicker").append(this._getTimePickerTemplate()),i=k("
      ").addClass("list-unstyled"),r=k("
    • ").addClass("picker-switch"+(this._options.collapse?" accordion-toggle":"")).append(this._getToolbar());return this._options.inline&&e.removeClass("dropdown-menu"),this.use24Hours&&e.addClass("usetwentyfour"),this._isEnabled("s")&&!this.use24Hours&&e.addClass("wider"),this._options.sideBySide&&this._hasDate()&&this._hasTime()?(e.addClass("timepicker-sbs"),"top"===this._options.toolbarPlacement&&e.append(r),e.append(k("
      ").addClass("row").append(t.addClass("col-md-6")).append(n.addClass("col-md-6"))),"bottom"!==this._options.toolbarPlacement&&"default"!==this._options.toolbarPlacement||e.append(r),e):("top"===this._options.toolbarPlacement&&i.append(r),this._hasDate()&&i.append(k("
    • ").addClass(this._options.collapse&&this._hasTime()?"collapse":"").addClass(this._options.collapse&&this._hasTime()&&"times"===this._options.viewMode?"":"show").append(t)),"default"===this._options.toolbarPlacement&&i.append(r),this._hasTime()&&i.append(k("
    • ").addClass(this._options.collapse&&this._hasDate()?"collapse":"").addClass(this._options.collapse&&this._hasDate()&&"times"===this._options.viewMode?"show":"").append(n)),"bottom"===this._options.toolbarPlacement&&i.append(r),e.append(i))},r.prototype._place=function(e){var t=e&&e.data&&e.data.picker||this,n=t._options.widgetPositioning.vertical,i=t._options.widgetPositioning.horizontal,r=void 0,o=(t.component&&t.component.length?t.component:t._element).position(),a=(t.component&&t.component.length?t.component:t._element).offset();if(t._options.widgetParent)r=t._options.widgetParent.append(t.widget);else if(t._element.is("input"))r=t._element.after(t.widget).parent();else{if(t._options.inline)return void(r=t._element.append(t.widget));r=t._element,t._element.children().first().after(t.widget)}if("auto"===n&&(n=a.top+1.5*t.widget.height()>=k(window).height()+k(window).scrollTop()&&t.widget.height()+t._element.outerHeight()k(window).width()?"right":"left"),"top"===n?t.widget.addClass("top").removeClass("bottom"):t.widget.addClass("bottom").removeClass("top"),"right"===i?t.widget.addClass("float-right"):t.widget.removeClass("float-right"),"relative"!==r.css("position")&&(r=r.parents().filter(function(){return"relative"===k(this).css("position")}).first()),0===r.length)throw new Error("datetimepicker component should be placed within a relative positioned container");t.widget.css({top:"top"===n?"auto":o.top+t._element.outerHeight()+"px",bottom:"top"===n?r.outerHeight()-(r===t._element?0:o.top)+"px":"auto",left:"left"===i?(r===t._element?0:o.left)+"px":"auto",right:"left"===i?"auto":r.outerWidth()-t._element.outerWidth()-(r===t._element?0:o.left)+"px"})},r.prototype._fillDow=function(){var e=k("
    "),t=this._viewDate.clone().startOf("w").startOf("d");for(!0===this._options.calendarWeeks&&e.append(k(""),this._options.calendarWeeks&&r.append('"),n.push(r)),o="",i.isBefore(this._viewDate,"M")&&(o+=" old"),i.isAfter(this._viewDate,"M")&&(o+=" new"),this._options.allowMultidate){var s=this._datesFormatted.indexOf(i.format("YYYY-MM-DD"));-1!==s&&i.isSame(this._datesFormatted[s],"d")&&!this.unset&&(o+=" active")}else i.isSame(this._getLastPickedDate(),"d")&&!this.unset&&(o+=" active");this._isValid(i,"d")||(o+=" disabled"),i.isSame(this.getMoment(),"d")&&(o+=" today"),0!==i.day()&&6!==i.day()||(o+=" weekend"),r.append('"),i.add(1,"d")}e.find("tbody").empty().append(n),this._updateMonths(),this._updateYears(),this._updateDecades()}},r.prototype._fillHours=function(){var e=this.widget.find(".timepicker-hours table"),t=this._viewDate.clone().startOf("d"),n=[],i=k("");for(11"),n.push(i)),i.append('"),t.add(1,"h");e.empty().append(n)},r.prototype._fillMinutes=function(){for(var e=this.widget.find(".timepicker-minutes table"),t=this._viewDate.clone().startOf("h"),n=[],i=1===this._options.stepping?5:this._options.stepping,r=k("");this._viewDate.isSame(t,"h");)t.minute()%(4*i)==0&&(r=k(""),n.push(r)),r.append('"),t.add(i,"m");e.empty().append(n)},r.prototype._fillSeconds=function(){for(var e=this.widget.find(".timepicker-seconds table"),t=this._viewDate.clone().startOf("m"),n=[],i=k("");this._viewDate.isSame(t,"m");)t.second()%20==0&&(i=k(""),n.push(i)),i.append('"),t.add(5,"s");e.empty().append(n)},r.prototype._fillTime=function(){var e=void 0,t=void 0,n=this.widget.find(".timepicker span[data-time-component]");this.use24Hours||(e=this.widget.find(".timepicker [data-action=togglePeriod]"),t=this._getLastPickedDate().clone().add(12<=this._getLastPickedDate().hours()?-12:12,"h"),e.text(this._getLastPickedDate().format("A")),this._isValid(t,"h")?e.removeClass("disabled"):e.addClass("disabled")),n.filter("[data-time-component=hours]").text(this._getLastPickedDate().format(this.use24Hours?"HH":"hh")),n.filter("[data-time-component=minutes]").text(this._getLastPickedDate().format("mm")),n.filter("[data-time-component=seconds]").text(this._getLastPickedDate().format("ss")),this._fillHours(),this._fillMinutes(),this._fillSeconds()},r.prototype._doAction=function(e,t){var n=this._getLastPickedDate();if(k(e.currentTarget).is(".disabled"))return!1;switch(t=t||k(e.currentTarget).data("action")){case"next":var i=E.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.add(E.DatePickerModes[this.currentViewMode].NAV_STEP,i),this._fillDate(),this._viewUpdate(i);break;case"previous":var r=E.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.subtract(E.DatePickerModes[this.currentViewMode].NAV_STEP,r),this._fillDate(),this._viewUpdate(r);break;case"pickerSwitch":this._showMode(1);break;case"selectMonth":var o=k(e.target).closest("tbody").find("span").index(k(e.target));this._viewDate.month(o),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()).month(this._viewDate.month()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("M");break;case"selectYear":var a=parseInt(k(e.target).text(),10)||0;this._viewDate.year(a),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDecade":var s=parseInt(k(e.target).data("selection"),10)||0;this._viewDate.year(s),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDay":var l=this._viewDate.clone();k(e.target).is(".old")&&l.subtract(1,"M"),k(e.target).is(".new")&&l.add(1,"M");var u=l.date(parseInt(k(e.target).text(),10)),c=0;this._options.allowMultidate?-1!==(c=this._datesFormatted.indexOf(u.format("YYYY-MM-DD")))?this._setValue(null,c):this._setValue(u,this._getLastPickedDateIndex()+1):this._setValue(u,this._getLastPickedDateIndex()),this._hasTime()||this._options.keepOpen||this._options.inline||this._options.allowMultidate||this.hide();break;case"incrementHours":var d=n.clone().add(1,"h");this._isValid(d,"h")&&this._setValue(d,this._getLastPickedDateIndex());break;case"incrementMinutes":var h=n.clone().add(this._options.stepping,"m");this._isValid(h,"m")&&this._setValue(h,this._getLastPickedDateIndex());break;case"incrementSeconds":var f=n.clone().add(1,"s");this._isValid(f,"s")&&this._setValue(f,this._getLastPickedDateIndex());break;case"decrementHours":var p=n.clone().subtract(1,"h");this._isValid(p,"h")&&this._setValue(p,this._getLastPickedDateIndex());break;case"decrementMinutes":var m=n.clone().subtract(this._options.stepping,"m");this._isValid(m,"m")&&this._setValue(m,this._getLastPickedDateIndex());break;case"decrementSeconds":var g=n.clone().subtract(1,"s");this._isValid(g,"s")&&this._setValue(g,this._getLastPickedDateIndex());break;case"togglePeriod":this._setValue(n.clone().add(12<=n.hours()?-12:12,"h"),this._getLastPickedDateIndex());break;case"togglePicker":var v=k(e.target),y=v.closest("a"),b=v.closest("ul"),_=b.find(".show"),w=b.find(".collapse:not(.show)"),x=v.is("span")?v:v.find("span"),T=void 0;if(_&&_.length){if((T=_.data("collapse"))&&T.transitioning)return!0;_.collapse?(_.collapse("hide"),w.collapse("show")):(_.removeClass("show"),w.addClass("show")),x.toggleClass(this._options.icons.time+" "+this._options.icons.date),x.hasClass(this._options.icons.date)?y.attr("title",this._options.tooltips.selectDate):y.attr("title",this._options.tooltips.selectTime)}break;case"showPicker":this.widget.find(".timepicker > div:not(.timepicker-picker)").hide(),this.widget.find(".timepicker .timepicker-picker").show();break;case"showHours":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-hours").show();break;case"showMinutes":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-minutes").show();break;case"showSeconds":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-seconds").show();break;case"selectHour":var C=parseInt(k(e.target).text(),10);this.use24Hours||(12<=n.hours()?12!==C&&(C+=12):12===C&&(C=0)),this._setValue(n.clone().hours(C),this._getLastPickedDateIndex()),this._isEnabled("a")||this._isEnabled("m")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"selectMinute":this._setValue(n.clone().minutes(parseInt(k(e.target).text(),10)),this._getLastPickedDateIndex()),this._isEnabled("a")||this._isEnabled("s")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"selectSecond":this._setValue(n.clone().seconds(parseInt(k(e.target).text(),10)),this._getLastPickedDateIndex()),this._isEnabled("a")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"clear":this.clear();break;case"close":this.hide();break;case"today":var D=this.getMoment();this._isValid(D,"d")&&this._setValue(D,this._getLastPickedDateIndex())}return!1},r.prototype.hide=function(){var t=!1;this.widget&&(this.widget.find(".collapse").each(function(){var e=k(this).data("collapse");return!e||!e.transitioning||!(t=!0)}),t||(this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this.widget.hide(),k(window).off("resize",this._place()),this.widget.off("click","[data-action]"),this.widget.off("mousedown",!1),this.widget.remove(),this.widget=!1,this._notifyEvent({type:E.Event.HIDE,date:this._getLastPickedDate().clone()}),void 0!==this.input&&this.input.blur(),this._viewDate=this._getLastPickedDate().clone()))},r.prototype.show=function(){var e=void 0,t={year:function(e){return e.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(e){return e.date(1).hours(0).seconds(0).minutes(0)},day:function(e){return e.hours(0).seconds(0).minutes(0)},hour:function(e){return e.seconds(0).minutes(0)},minute:function(e){return e.seconds(0)}};if(void 0!==this.input){if(this.input.prop("disabled")||!this._options.ignoreReadonly&&this.input.prop("readonly")||this.widget)return;void 0!==this.input.val()&&0!==this.input.val().trim().length?this._setValue(this._parseInputDate(this.input.val().trim()),0):this.unset&&this._options.useCurrent&&(e=this.getMoment(),"string"==typeof this._options.useCurrent&&(e=t[this._options.useCurrent](e)),this._setValue(e,0))}else this.unset&&this._options.useCurrent&&(e=this.getMoment(),"string"==typeof this._options.useCurrent&&(e=t[this._options.useCurrent](e)),this._setValue(e,0));this.widget=this._getTemplate(),this._fillDow(),this._fillMonths(),this.widget.find(".timepicker-hours").hide(),this.widget.find(".timepicker-minutes").hide(),this.widget.find(".timepicker-seconds").hide(),this._update(),this._showMode(),k(window).on("resize",{picker:this},this._place),this.widget.on("click","[data-action]",k.proxy(this._doAction,this)),this.widget.on("mousedown",!1),this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this._place(),this.widget.show(),void 0!==this.input&&this._options.focusOnShow&&!this.input.is(":focus")&&this.input.focus(),this._notifyEvent({type:E.Event.SHOW})},r.prototype.destroy=function(){this.hide(),this._element.removeData(E.DATA_KEY),this._element.removeData("date")},r.prototype.disable=function(){this.hide(),this.component&&this.component.hasClass("btn")&&this.component.addClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!0)},r.prototype.enable=function(){this.component&&this.component.hasClass("btn")&&this.component.removeClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!1)},r.prototype.toolbarPlacement=function(e){if(0===arguments.length)return this._options.toolbarPlacement;if("string"!=typeof e)throw new TypeError("toolbarPlacement() expects a string parameter");if(-1===_.indexOf(e))throw new TypeError("toolbarPlacement() parameter must be one of ("+_.join(", ")+") value");this._options.toolbarPlacement=e,this.widget&&(this.hide(),this.show())},r.prototype.widgetPositioning=function(e){if(0===arguments.length)return k.extend({},this._options.widgetPositioning);if("[object Object]"!=={}.toString.call(e))throw new TypeError("widgetPositioning() expects an object variable");if(e.horizontal){if("string"!=typeof e.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(e.horizontal=e.horizontal.toLowerCase(),-1===b.indexOf(e.horizontal))throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+b.join(", ")+")");this._options.widgetPositioning.horizontal=e.horizontal}if(e.vertical){if("string"!=typeof e.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(e.vertical=e.vertical.toLowerCase(),-1===y.indexOf(e.vertical))throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+y.join(", ")+")");this._options.widgetPositioning.vertical=e.vertical}this._update()},r.prototype.widgetParent=function(e){if(0===arguments.length)return this._options.widgetParent;if("string"==typeof e&&(e=k(e)),null!==e&&"string"!=typeof e&&!(e instanceof k))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");this._options.widgetParent=e,this.widget&&(this.hide(),this.show())},r._jQueryHandleThis=function(e,t,n){var i=k(e).data(E.DATA_KEY);if("object"===(void 0===t?"undefined":o(t))&&k.extend({},E.Default,t),i||(i=new r(k(e),t),k(e).data(E.DATA_KEY,i)),"string"==typeof t){if(void 0===i[t])throw new Error('No method named "'+t+'"');return void 0===n?i[t]():i[t](n)}},r._jQueryInterface=function(e,t){return 1===this.length?r._jQueryHandleThis(this[0],e,t):this.each(function(){r._jQueryHandleThis(this,e,t)})},r}(E),k(document).on(E.Event.CLICK_DATA_API,E.Selector.DATA_TOGGLE,function(){var e=w(k(this));0!==e.length&&x._jQueryInterface.call(e,"toggle")}).on(E.Event.CHANGE,"."+E.ClassName.INPUT,function(e){var t=w(k(this));0!==t.length&&x._jQueryInterface.call(t,"_change",e)}).on(E.Event.BLUR,"."+E.ClassName.INPUT,function(e){var t=w(k(this)),n=t.data(E.DATA_KEY);0!==t.length&&(n._options.debug||window.debug||x._jQueryInterface.call(t,"hide",e))}).on(E.Event.KEYDOWN,"."+E.ClassName.INPUT,function(e){var t=w(k(this));0!==t.length&&x._jQueryInterface.call(t,"_keydown",e)}).on(E.Event.KEYUP,"."+E.ClassName.INPUT,function(e){var t=w(k(this));0!==t.length&&x._jQueryInterface.call(t,"_keyup",e)}).on(E.Event.FOCUS,"."+E.ClassName.INPUT,function(e){var t=w(k(this)),n=t.data(E.DATA_KEY);0!==t.length&&n._options.allowInputToggle&&x._jQueryInterface.call(t,"show",e)}),k.fn[E.NAME]=x._jQueryInterface,k.fn[E.NAME].Constructor=x,k.fn[E.NAME].noConflict=function(){return k.fn[E.NAME]=v,x._jQueryInterface}}();var Menu={init:function(){$(function(){Menu.itemClick()})},itemClick:function(){$(".menu-button").click(function(e){e.preventDefault(),$(".menu-item").is(":visible")?$(".menu-item").css("display",""):$(".menu-item").show()})}};Menu.init(),ko.bindingHandlers.modal={init:function(e,t){$(e).modal({show:!1});var n=t();ko.isObservable(n)&&$(e).on("hidden.bs.modal",function(){n(!1)})},update:function(e,t){var n=t();ko.utils.unwrapObservable(n)?$(e).modal("show"):$(e).modal("hide")}},ko.components.register("picker",{viewModel:function(n){var i=this;this.textTerm=ko.observable("").extend({rateLimit:500}),this.minSearchText=ko.observable(n.minSearchText||2),this.multipleSelect=ko.observable(n.multipleSelect||!1),this.searchInputPlaceholder=ko.observable(n.searchInputPlaceholder||"Enter "+this.minSearchText()+" or more characters"),this.selectedItemsTitle=ko.observable(n.selectedItemsTitle||"Selected: "),this.searchResultTitle=ko.observable(n.searchResultTitle||"Search result: "),this.suggestedItemsTitle=ko.observable(n.suggestedItemsTitle||"Suggested items: "),this.noItemSelectedTitle=ko.observable(n.noItemSelectedTitle||"No item/s selected"),this.showAllItemsTitle=ko.observable(n.showAllItemsTitle||"more"),this.allowSuggestedItems=ko.observable(n.allowSuggestedItems&&n.url||!1),this.topSuggestedItems=ko.observable(n.topSuggestedItems||5),this.allowItemAlreadySelectedNotification=ko.observable(n.allowItemAlreadySelectedNotification||!0),this.itemAlreadySelectedTitle=ko.observable(n.itemAlreadySelectedTitle||"item already selected"),this.searchResult=ko.observableArray([]),this.selectedResult=ko.observableArray(n.selectedItems||[]),this.suggestedResult=ko.observableArray([]),this.loading=ko.observable(!1),this.isVisibleEditDialog=ko.observable(!1),this.editedItem=ko.observable(""),this.editedItemOriginal=ko.observable("");var e=ko.toJSON(this.selectedResult);!0===this.multipleSelect()?0===this.selectedResult().length?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(e):0===this.selectedResult().length?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(this.selectedResult()[0]),this.textTerm.subscribe(function(t){""===t.trim()&&i.searchResult([]),""!==t.trim()&&t.trim().length>=i.minSearchText()&&(n.url?(i.loading(!0),$.get(n.url+"="+t,function(e){-1===e.indexOf(t)&&e.push(t),i.searchResult(e),i.loading(!1)})):i.searchResult([t]))}),this.notify=function(e){toastr.options.closeButton=!0,toastr.options.preventDuplicates=!0,toastr.info(e+" "+this.itemAlreadySelectedTitle())},this.notifyError=function(e){toastr.options.closeButton=!0,toastr.options.preventDuplicates=!0,toastr.error(e)},this.add=function(e){e=e.replace(/'/g,"").replace(/"/g,""),-1

    Loading..

    \x3c!-- ko foreach: suggestedResult --\x3e\x3c!-- /ko --\x3e
    '}),ko.applyBindings(),Holder.addTheme("thumb",{bg:"#55595c",fg:"#eceeef",text:"Thumbnail"});var FormMvc={allowValidateHiddenField:function(e){e.data("validator").settings.ignore=""},disableEnter:function(e){e.on("keyup keypress",function(e){if(13===(e.keyCode||e.which))return e.preventDefault(),!1})}};$(function(){$(".single-select").removeAttr("multiple"),$('[data-toggle="tooltip"]').tooltip()});var JSONTree=function(){var t={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},e=0,n=0;this.create=function(e,t){return n+=1,p(s(e,0,!1),{class:"jstValue"})};var o=function(e){return e.replace(/[&<>'"]/g,function(e){return t[e]})},a=function(){return n+"_"+e++},s=function(e,t,n){if(null===e)return d(n?t:0);switch(typeof e){case"boolean":return c(e,n?t:0);case"number":return u(e,n?t:0);case"string":return l(e,n?t:0);default:return e instanceof Array?r(e,t,n):i(e,t,n)}},i=function(t,n,e){var i=a(),r=Object.keys(t).map(function(e){return h(e,t[e],n+1,!0)}).join(f()),o=[g("{",e?n:0,i),p(r,{id:i}),v("}",n)].join("\n");return p(o,{})},r=function(e,t,n){var i=a(),r=e.map(function(e){return s(e,t+1,!0)}).join(f());return[g("[",n?t:0,i),p(r,{id:i}),v("]",t)].join("\n")},l=function(e,t){var n=o(JSON.stringify(e));return p(y(n,t),{class:"jstStr"})},u=function(e,t){return p(y(e,t),{class:"jstNum"})},c=function(e,t){return p(y(e,t),{class:"jstBool"})},d=function(e){return p(y("null",e),{class:"jstNull"})},h=function(e,t,n){var i=y(o(JSON.stringify(e))+": ",n),r=p(s(t,n,!1),{});return p(i+r,{class:"jstProperty"})},f=function(){return p(",\n",{class:"jstComma"})},p=function(e,t){return m("span",t,e)},m=function(e,t,n){return"<"+e+Object.keys(t).map(function(e){return" "+e+'="'+t[e]+'"'}).join("")+">"+n+""},g=function(e,t,n){return p(y(e,t),{class:"jstBracket"})+p("",{class:"jstFold",onclick:"JSONTree.toggle('"+n+"')"})};this.toggle=function(e){var t=document.getElementById(e),n=t.parentNode,i=t.previousElementSibling;""===t.className?(t.className="jstHiddenBlock",n.className="jstFolded",i.className="jstExpand"):(t.className="",n.className="",i.className="jstFold")};var v=function(e,t){return p(y(e,t),{})},y=function(e,t){return Array(2*t+1).join(" ")+e};return this}();$(function(){$(".local-datetime").each(function(){var e=$(this),t=parseInt(e.attr("data-utc"),10)||0;if(t){var n=moment.utc(t).local().format("DD MMM YYYY HH:mm");e.text(n)}}),$('[data-toggle="tooltip"]').tooltip()}),$(function(){$(".row-error-detail>td").each(function(){var e=$(this).data("error-json"),t=JSONTree.create(JSON.parse(e));$(this).html(t)}),$(".btn-error-detail").click(function(e){e.preventDefault();var t=$(this).data("error-id");return $(".row-error-detail[data-error-id="+t+"]").is(":visible")?$(".row-error-detail[data-error-id="+t+"]").addClass("d-none"):$(".row-error-detail[data-error-id="+t+"]").removeClass("d-none"),!1})}); \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/AccountOptions.cs b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/AccountOptions.cs similarity index 94% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/AccountOptions.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Configuration/AccountOptions.cs index 5dc790506..f3ee14afb 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/AccountOptions.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/AccountOptions.cs @@ -6,7 +6,7 @@ using System; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account +namespace Skoruba.IdentityServer4.STS.Identity.Configuration { public class AccountOptions { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentOptions.cs b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/ConsentOptions.cs similarity index 92% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentOptions.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Configuration/ConsentOptions.cs index de2ceac7a..af8ac4fec 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentOptions.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/ConsentOptions.cs @@ -4,7 +4,7 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI // Modified by Jan Škoruba -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent +namespace Skoruba.IdentityServer4.STS.Identity.Configuration { public class ConsentOptions { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Configuration/Constants/ConfigurationConsts.cs b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/Constants/ConfigurationConsts.cs index d30f2a050..eeb406775 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Configuration/Constants/ConfigurationConsts.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/Constants/ConfigurationConsts.cs @@ -3,7 +3,8 @@ public class ConfigurationConsts { public const string AdminConnectionStringKey = "AdminConnection"; - + public const string SendgridConnectionStringKey = "Sendgrid"; + public const string ResourcesPath = "Resources"; } } \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Configuration/ExternalProvidersConfiguration.cs b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/ExternalProvidersConfiguration.cs new file mode 100644 index 000000000..c3a3833e2 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/ExternalProvidersConfiguration.cs @@ -0,0 +1,9 @@ +namespace Skoruba.IdentityServer4.STS.Identity.Configuration +{ + public class ExternalProvidersConfiguration + { + public bool UseGitHubProvider { get; set; } + public string GitHubClientId { get; set; } + public string GitHubClientSecret { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Configuration/SendgridConfiguration.cs b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/SendgridConfiguration.cs new file mode 100644 index 000000000..179c885b3 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/SendgridConfiguration.cs @@ -0,0 +1,8 @@ +namespace Skoruba.IdentityServer4.STS.Identity.Configuration +{ + public class SendgridConfiguration + { + public string SourceEmail { get; set; } + public string SourceName { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Configuration/SmtpConfiguration.cs b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/SmtpConfiguration.cs new file mode 100644 index 000000000..154dbf7e7 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Configuration/SmtpConfiguration.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Skoruba.IdentityServer4.STS.Identity.Configuration +{ + public class SmtpConfiguration + { + public string Host { get; set; } + public string Login { get; set; } + public string Password { get; set; } + public int Port { get; set; } = 587; // default smtp port + public bool UseSSL { get; set; } = true; + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/AccountController.cs b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/AccountController.cs similarity index 52% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/AccountController.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Controllers/AccountController.cs index f31c5126a..9007576b3 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/AccountController.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/AccountController.cs @@ -6,6 +6,8 @@ using System; using System.Linq; +using System.Security.Claims; +using System.Text.Encodings.Web; using System.Threading.Tasks; using IdentityModel; using IdentityServer4.Events; @@ -16,10 +18,15 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Localization; using Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity; +using Skoruba.IdentityServer4.STS.Identity.Configuration; +using Skoruba.IdentityServer4.STS.Identity.Helpers; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Account; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account +namespace Skoruba.IdentityServer4.STS.Identity.Controllers { [SecurityHeaders] [AllowAnonymous] @@ -31,6 +38,8 @@ public class AccountController : Controller private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; + private readonly IEmailSender _emailSender; + private readonly IStringLocalizer _localizer; public AccountController( UserManager userManager, @@ -38,7 +47,9 @@ public AccountController( IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, - IEventService events) + IEventService events, + IEmailSender emailSender, + IStringLocalizer localizer) { _userManager = userManager; _signInManager = signInManager; @@ -46,6 +57,8 @@ public AccountController( _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; + _emailSender = emailSender; + _localizer = localizer; } /// @@ -56,11 +69,11 @@ public async Task Login(string returnUrl) { // build a model so we know what to show on the login page var vm = await BuildLoginViewModelAsync(returnUrl); - - if (vm.IsExternalLoginOnly) + + if (vm.EnableLocalLogin == false && vm.ExternalProviders.Count() == 1) { - // we only have one option for logging in and it's an external provider - return RedirectToAction("Challenge", "External", new { provider = vm.ExternalLoginScheme, returnUrl }); + // only one option for logging in + return ExternalLogin(vm.ExternalProviders.First().AuthenticationScheme, returnUrl); } return View(vm); @@ -96,11 +109,9 @@ public async Task Login(LoginInputModel model, string button) return Redirect(model.ReturnUrl); } - else - { - // since we don't have a valid context, then we just go back to the home page - return Redirect("~/"); - } + + // since we don't have a valid context, then we just go back to the home page + return Redirect("~/"); } if (ModelState.IsValid) @@ -129,15 +140,24 @@ public async Task Login(LoginInputModel model, string button) { return Redirect(model.ReturnUrl); } - else if (string.IsNullOrEmpty(model.ReturnUrl)) + + if (string.IsNullOrEmpty(model.ReturnUrl)) { return Redirect("~/"); } - else - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } + + // user might have clicked on a malicious link - should be logged + throw new Exception("invalid return URL"); + } + + if (result.RequiresTwoFactor) + { + return RedirectToAction(nameof(LoginWith2fa), new { ReturnUrl = model.ReturnUrl, RememberMe = model.RememberLogin }); + } + + if (result.IsLockedOut) + { + return View("Lockout"); } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials")); @@ -149,7 +169,7 @@ public async Task Login(LoginInputModel model, string button) return View(vm); } - + /// /// Show logout page /// @@ -203,11 +223,318 @@ public async Task Logout(LogoutInputModel model) return View("LoggedOut", vm); } + [HttpGet] + [AllowAnonymous] + public async Task ConfirmEmail(string userId, string code) + { + if (userId == null || code == null) + { + return View("Error"); + } + var user = await _userManager.FindByIdAsync(userId); + if (user == null) + { + return View("Error"); + } + var result = await _userManager.ConfirmEmailAsync(user, code); + return View(result.Succeeded ? "ConfirmEmail" : "Error"); + } + + [HttpGet] + [AllowAnonymous] + public IActionResult ForgotPassword() + { + return View(); + } + + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ForgotPassword(ForgotPasswordViewModel model) + { + if (ModelState.IsValid) + { + var user = await _userManager.FindByEmailAsync(model.Email); + if (user == null || !await _userManager.IsEmailConfirmedAsync(user)) + { + ModelState.AddModelError(string.Empty, _localizer["EmailNotFound"]); + + return View(model); + } + + var code = await _userManager.GeneratePasswordResetTokenAsync(user); + var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code }, HttpContext.Request.Scheme); + + await _emailSender.SendEmailAsync(model.Email, "Reset Password", $"Please reset your password by clicking here."); + + + return View("ForgotPasswordConfirmation"); + } + + return View(model); + } + + [HttpGet] + [AllowAnonymous] + public IActionResult ResetPassword(string code = null) + { + return code == null ? View("Error") : View(); + } + + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ResetPassword(ResetPasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await _userManager.FindByEmailAsync(model.Email); + if (user == null) + { + // Don't reveal that the user does not exist + return RedirectToAction(nameof(ResetPasswordConfirmation), "Account"); + } + var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password); + if (result.Succeeded) + { + return RedirectToAction(nameof(ResetPasswordConfirmation), "Account"); + } + + AddErrors(result); + + return View(); + } + + [HttpGet] + [AllowAnonymous] + public IActionResult ResetPasswordConfirmation() + { + return View(); + } + + [HttpGet] + [AllowAnonymous] + public IActionResult ForgotPasswordConfirmation() + { + return View(); + } + + [HttpGet] + [AllowAnonymous] + public async Task ExternalLoginCallback(string returnUrl = null, string remoteError = null) + { + if (remoteError != null) + { + ModelState.AddModelError(string.Empty, _localizer["ErrorExternalProvider", remoteError]); + + return View(nameof(Login)); + } + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + return RedirectToAction(nameof(Login)); + } + + // Sign in the user with this external login provider if the user already has a login. + var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false); + if (result.Succeeded) + { + return RedirectToLocal(returnUrl); + } + + // If the user does not have an account, then ask the user to create an account. + ViewData["ReturnUrl"] = returnUrl; + ViewData["LoginProvider"] = info.LoginProvider; + var email = info.Principal.FindFirstValue(ClaimTypes.Email); + + return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email }); + } + + [HttpPost] + [HttpGet] + [AllowAnonymous] + public IActionResult ExternalLogin(string provider, string returnUrl = null) + { + // Request a redirect to the external login provider. + var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + + return Challenge(properties, provider); + } + + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null) + { + if (ModelState.IsValid) + { + // Get the information about the user from the external login provider + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + return View("ExternalLoginFailure"); + } + + var user = new UserIdentity + { + UserName = model.UserName, + Email = model.Email + }; + + var result = await _userManager.CreateAsync(user); + if (result.Succeeded) + { + result = await _userManager.AddLoginAsync(user, info); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + + return RedirectToLocal(returnUrl); + } + } + + AddErrors(result); + } + + ViewData["ReturnUrl"] = returnUrl; + + return View(model); + } + + [HttpGet] + [AllowAnonymous] + public async Task LoginWithRecoveryCode(string returnUrl = null) + { + // Ensure the user has gone through the username & password screen first + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + throw new InvalidOperationException(_localizer["Unable2FA"]); + } + + var model = new LoginWithRecoveryCodeViewModel() + { + ReturnUrl = returnUrl + }; + + return View(model); + } + + [HttpPost] + [AllowAnonymous] + public async Task LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + throw new InvalidOperationException(_localizer["Unable2FA"]); + } + + var recoveryCode = model.RecoveryCode.Replace(" ", string.Empty); + + var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode); + + if (result.Succeeded) + { + return LocalRedirect(model.ReturnUrl); + } + + if (result.IsLockedOut) + { + return View("Lockout"); + } + + ModelState.AddModelError(string.Empty, _localizer["InvalidRecoveryCode"]); + + return View(model); + } + + [HttpGet] + [AllowAnonymous] + public async Task LoginWith2fa(bool rememberMe, string returnUrl = null) + { + // Ensure the user has gone through the username & password screen first + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + + if (user == null) + { + throw new InvalidOperationException(_localizer["Unable2FA"]); + } + + var model = new LoginWith2faViewModel() + { + ReturnUrl = returnUrl, + RememberMe = rememberMe + }; + + return View(model); + } + + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task LoginWith2fa(LoginWith2faViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + throw new InvalidOperationException(_localizer["Unable2FA"]); + } + + var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty); + + var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, model.RememberMe, model.RememberMachine); + + if (result.Succeeded) + { + return LocalRedirect(model.ReturnUrl); + } + + if (result.IsLockedOut) + { + return View("Lockout"); + } + + ModelState.AddModelError(string.Empty, _localizer["InvalidAuthenticatorCode"]); + + return View(model); + } /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ + private IActionResult RedirectToLocal(string returnUrl) + { + if (Url.IsLocalUrl(returnUrl)) + { + return Redirect(returnUrl); + } + + return RedirectToAction(nameof(HomeController.Index), "Home"); + } + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + private async Task BuildLoginViewModelAsync(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentController.cs b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/ConsentController.cs similarity index 97% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentController.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Controllers/ConsentController.cs index 517f7d2b6..6ee3ad7ce 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentController.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/ConsentController.cs @@ -14,9 +14,12 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Skoruba.IdentityServer4.STS.Identity.Quickstart.Account; +using Skoruba.IdentityServer4.STS.Identity.Configuration; +using Skoruba.IdentityServer4.STS.Identity.Helpers; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Account; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Consent; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent +namespace Skoruba.IdentityServer4.STS.Identity.Controllers { /// /// This controller processes the consent UI diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Device/DeviceController.cs b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/DeviceController.cs similarity index 96% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Device/DeviceController.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Controllers/DeviceController.cs index 33ee0359d..ec84eea39 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Device/DeviceController.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/DeviceController.cs @@ -15,9 +15,12 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent; +using Skoruba.IdentityServer4.STS.Identity.Configuration; +using Skoruba.IdentityServer4.STS.Identity.Helpers; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Consent; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Device; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Device +namespace Skoruba.IdentityServer4.STS.Identity.Controllers { [Authorize] [SecurityHeaders] diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Diagnostics/DiagnosticsController.cs b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/DiagnosticsController.cs similarity index 85% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Diagnostics/DiagnosticsController.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Controllers/DiagnosticsController.cs index b92aa70d7..355f6fad1 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Diagnostics/DiagnosticsController.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/DiagnosticsController.cs @@ -9,8 +9,10 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Skoruba.IdentityServer4.STS.Identity.Helpers; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Diagnostics; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Diagnostics +namespace Skoruba.IdentityServer4.STS.Identity.Controllers { [SecurityHeaders] [Authorize] diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Grants/GrantsController.cs b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/GrantsController.cs similarity index 94% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Grants/GrantsController.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Controllers/GrantsController.cs index 56c690afe..aef46f3b2 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Grants/GrantsController.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/GrantsController.cs @@ -11,8 +11,10 @@ using IdentityServer4.Stores; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Skoruba.IdentityServer4.STS.Identity.Helpers; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Grants; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Grants +namespace Skoruba.IdentityServer4.STS.Identity.Controllers { /// /// This sample controller allows a user to revoke grants given to clients diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Home/HomeController.cs b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/HomeController.cs similarity index 91% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Home/HomeController.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Controllers/HomeController.cs index 5d20527e2..5d0d2936d 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Home/HomeController.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/HomeController.cs @@ -10,8 +10,10 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc; +using Skoruba.IdentityServer4.STS.Identity.Helpers; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Home; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Home +namespace Skoruba.IdentityServer4.STS.Identity.Controllers { [SecurityHeaders] public class HomeController : Controller diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Controllers/ManageController.cs b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/ManageController.cs new file mode 100644 index 000000000..3a000f755 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Controllers/ManageController.cs @@ -0,0 +1,666 @@ +using System; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage; + +namespace Skoruba.IdentityServer4.STS.Identity.Controllers +{ + public class ManageController : Controller + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + private readonly ILogger _logger; + private readonly IStringLocalizer _localizer; + private readonly UrlEncoder _urlEncoder; + + private const string RecoveryCodesKey = nameof(RecoveryCodesKey); + private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; + + [TempData] + public string StatusMessage { get; set; } + + public ManageController(UserManager userManager, SignInManager signInManager, IEmailSender emailSender, ILogger logger, IStringLocalizer localizer, UrlEncoder urlEncoder) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + _logger = logger; + _localizer = localizer; + _urlEncoder = urlEncoder; + } + + [HttpGet] + public async Task Index() + { + var user = await _userManager.GetUserAsync(User); + + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var model = new IndexViewModel + { + Username = user.UserName, + Email = user.Email, + PhoneNumber = user.PhoneNumber, + IsEmailConfirmed = user.EmailConfirmed, + StatusMessage = StatusMessage + }; + + return View(model); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Index(IndexViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var email = user.Email; + if (model.Email != email) + { + var setEmailResult = await _userManager.SetEmailAsync(user, model.Email); + if (!setEmailResult.Succeeded) + { + throw new ApplicationException(_localizer["ErrorSettingEmail", user.Id]); + } + } + + var phoneNumber = user.PhoneNumber; + if (model.PhoneNumber != phoneNumber) + { + var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, model.PhoneNumber); + if (!setPhoneResult.Succeeded) + { + throw new ApplicationException(_localizer["ErrorSettingPhone", user.Id]); + } + } + + StatusMessage = _localizer["ProfileUpdated"]; + + return RedirectToAction(nameof(Index)); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SendVerificationEmail(IndexViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code }, HttpContext.Request.Scheme); + + await _emailSender.SendEmailAsync(model.Email, "Confirm your email", $"Please confirm your account by clicking here."); + + StatusMessage = _localizer["VerificationSent"]; + + return RedirectToAction(nameof(Index)); + } + + [HttpGet] + public async Task ChangePassword() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var hasPassword = await _userManager.HasPasswordAsync(user); + if (!hasPassword) + { + return RedirectToAction(nameof(SetPassword)); + } + + var model = new ChangePasswordViewModel { StatusMessage = StatusMessage }; + return View(model); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task ChangePassword(ChangePasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var changePasswordResult = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); + if (!changePasswordResult.Succeeded) + { + AddErrors(changePasswordResult); + return View(model); + } + + await _signInManager.RefreshSignInAsync(user); + _logger.LogInformation(_localizer["PasswordChangedLog", user.UserName]); + + StatusMessage = _localizer["PasswordChanged"]; + + return RedirectToAction(nameof(ChangePassword)); + } + + [HttpGet] + public async Task SetPassword() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var hasPassword = await _userManager.HasPasswordAsync(user); + + if (hasPassword) + { + return RedirectToAction(nameof(ChangePassword)); + } + + var model = new SetPasswordViewModel { StatusMessage = StatusMessage }; + return View(model); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SetPassword(SetPasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var addPasswordResult = await _userManager.AddPasswordAsync(user, model.NewPassword); + if (!addPasswordResult.Succeeded) + { + AddErrors(addPasswordResult); + return View(model); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = _localizer["PasswordSet"]; + + return RedirectToAction(nameof(SetPassword)); + } + + [HttpGet] + public async Task PersonalData() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + return View(); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DownloadPersonalData() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + _logger.LogInformation(_localizer["AskForPersonalDataLog"], _userManager.GetUserId(User)); + + var personalDataProps = typeof(UserIdentity).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); + var personalData = personalDataProps.ToDictionary(p => p.Name, p => p.GetValue(user)?.ToString() ?? "null"); + + Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); + return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), "text/json"); + } + + [HttpGet] + public async Task DeletePersonalData() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var deletePersonalDataViewModel = new DeletePersonalDataViewModel + { + RequirePassword = await _userManager.HasPasswordAsync(user) + }; + + return View(deletePersonalDataViewModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DeletePersonalData(DeletePersonalDataViewModel deletePersonalDataViewModel) + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + deletePersonalDataViewModel.RequirePassword = await _userManager.HasPasswordAsync(user); + if (deletePersonalDataViewModel.RequirePassword) + { + if (!await _userManager.CheckPasswordAsync(user, deletePersonalDataViewModel.Password)) + { + ModelState.AddModelError(string.Empty, _localizer["PasswordNotCorrect"]); + return View(deletePersonalDataViewModel); + } + } + + var result = await _userManager.DeleteAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException(_localizer["ErrorDeletingUser", user.Id]); + } + + await _signInManager.SignOutAsync(); + + _logger.LogInformation(_localizer["DeletePersonalData"], userId); + + return Redirect("~/"); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RemoveLogin(RemoveLoginViewModel model) + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var result = await _userManager.RemoveLoginAsync(user, model.LoginProvider, model.ProviderKey); + if (!result.Succeeded) + { + throw new ApplicationException(_localizer["ErrorRemovingExternalLogin", user.Id]); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = _localizer["ExternalLoginRemoved"]; + + return RedirectToAction(nameof(ExternalLogins)); + } + + [HttpGet] + public async Task LinkLoginCallback() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var info = await _signInManager.GetExternalLoginInfoAsync(user.Id.ToString()); + if (info == null) + { + throw new ApplicationException(_localizer["ErrorLoadingExternalLogin", user.Id]); + } + + var result = await _userManager.AddLoginAsync(user, info); + if (!result.Succeeded) + { + AddErrors(result); + return View("LinkLoginFailure"); + } + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + StatusMessage = _localizer["ExternalLoginAdded"]; + + return RedirectToAction(nameof(ExternalLogins)); + } + + [HttpGet] + public async Task ExternalLogins() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var model = new ExternalLoginsViewModel + { + CurrentLogins = await _userManager.GetLoginsAsync(user) + }; + + model.OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) + .Where(auth => model.CurrentLogins.All(ul => auth.Name != ul.LoginProvider)) + .ToList(); + + model.ShowRemoveButton = await _userManager.HasPasswordAsync(user) || model.CurrentLogins.Count > 1; + model.StatusMessage = StatusMessage; + + return View(model); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task LinkLogin(string provider) + { + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + // Request a redirect to the external login provider to link a login for the current user + var redirectUrl = Url.Action(nameof(LinkLoginCallback)); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); + + return new ChallengeResult(provider, properties); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task GenerateRecoveryCodes() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + if (!user.TwoFactorEnabled) + { + AddError(_localizer["ErrorGenerateCodesWithout2FA"]); + return View(); + } + + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + + _logger.LogInformation(_localizer["UserGenerated2FACodes", user.Id]); + + var model = new ShowRecoveryCodesViewModel { RecoveryCodes = recoveryCodes.ToArray() }; + + return View(nameof(ShowRecoveryCodes), model); + } + + [HttpGet] + public IActionResult ShowRecoveryCodes() + { + var recoveryCodes = (string[])TempData[RecoveryCodesKey]; + if (recoveryCodes == null) + { + return RedirectToAction(nameof(TwoFactorAuthentication)); + } + + var model = new ShowRecoveryCodesViewModel { RecoveryCodes = recoveryCodes }; + + return View(model); + } + + [HttpGet] + public async Task TwoFactorAuthentication() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var model = new TwoFactorAuthenticationViewModel + { + HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null, + Is2faEnabled = user.TwoFactorEnabled, + RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user), + IsMachineRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user) + }; + + return View(model); + } + + [HttpPost] + public async Task ForgetTwoFactorClient() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + await _signInManager.ForgetTwoFactorClientAsync(); + + StatusMessage = _localizer["SuccessForgetBrowser2FA"]; + + return RedirectToAction(nameof(TwoFactorAuthentication)); + } + + [HttpGet] + public async Task Disable2faWarning() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + if (!user.TwoFactorEnabled) + { + throw new ApplicationException(_localizer["ErrorDisable2FA", user.Id]); + } + + return View(nameof(Disable2fa)); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Disable2fa() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); + if (!disable2faResult.Succeeded) + { + throw new ApplicationException(_localizer["ErrorDisable2FA", user.Id]); + } + + _logger.LogInformation(_localizer["SuccessDisabled2FA", user.Id]); + + return RedirectToAction(nameof(TwoFactorAuthentication)); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task ResetAuthenticator() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + await _userManager.SetTwoFactorEnabledAsync(user, false); + await _userManager.ResetAuthenticatorKeyAsync(user); + _logger.LogInformation(_localizer["SuccessResetAuthenticationKey", user.Id]); + + return RedirectToAction(nameof(EnableAuthenticator)); + } + + [HttpGet] + public IActionResult ResetAuthenticatorWarning() + { + return View(nameof(ResetAuthenticator)); + } + + [HttpGet] + public async Task EnableAuthenticator() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + var model = new EnableAuthenticatorViewModel(); + await LoadSharedKeyAndQrCodeUriAsync(user, model); + + return View(model); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EnableAuthenticator(EnableAuthenticatorViewModel model) + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + if (!ModelState.IsValid) + { + await LoadSharedKeyAndQrCodeUriAsync(user, model); + return View(model); + } + + var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty); + + var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( + user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); + + if (!is2faTokenValid) + { + ModelState.AddModelError(_localizer["ErrorCode"], _localizer["InvalidVerificationCode"]); + await LoadSharedKeyAndQrCodeUriAsync(user, model); + return View(model); + } + + await _userManager.SetTwoFactorEnabledAsync(user, true); + var userId = await _userManager.GetUserIdAsync(user); + + _logger.LogInformation(_localizer["SuccessUserEnabled2FA"], userId); + + StatusMessage = _localizer["AuthenticatorVerified"]; + + if (await _userManager.CountRecoveryCodesAsync(user) == 0) + { + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + TempData[RecoveryCodesKey] = recoveryCodes.ToArray(); + + return RedirectToAction(nameof(ShowRecoveryCodes)); + } + + return RedirectToAction(nameof(TwoFactorAuthentication)); + } + + [HttpGet] + public async Task GenerateRecoveryCodesWarning() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound(_localizer["UserNotFound", _userManager.GetUserId(User)]); + } + + if (!user.TwoFactorEnabled) + { + throw new ApplicationException(_localizer["Error2FANotEnabled", user.Id]); + } + + return View(nameof(GenerateRecoveryCodes)); + } + + private async Task LoadSharedKeyAndQrCodeUriAsync(UserIdentity user, EnableAuthenticatorViewModel model) + { + var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + if (string.IsNullOrEmpty(unformattedKey)) + { + await _userManager.ResetAuthenticatorKeyAsync(user); + unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + } + + model.SharedKey = FormatKey(unformattedKey); + model.AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey); + } + + private string FormatKey(string unformattedKey) + { + var result = new StringBuilder(); + var currentPosition = 0; + + while (currentPosition + 4 < unformattedKey.Length) + { + result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); + currentPosition += 4; + } + + if (currentPosition < unformattedKey.Length) + { + result.Append(unformattedKey.Substring(currentPosition)); + } + + return result.ToString().ToLowerInvariant(); + } + + private string GenerateQrCodeUri(string email, string unformattedKey) + { + return string.Format( + AuthenticatorUriFormat, + _urlEncoder.Encode("Skoruba.IdentityServer4.STS.Identity"), + _urlEncoder.Encode(email), + unformattedKey); + } + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + private void AddError(string description, string title = "") + { + ModelState.AddModelError(title, description); + } + } +} \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Extensions.cs b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/Extensions.cs similarity index 94% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Extensions.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Helpers/Extensions.cs index 27540ea7b..239b53d46 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Extensions.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/Extensions.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using IdentityServer4.Stores; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart +namespace Skoruba.IdentityServer4.STS.Identity.Helpers { public static class Extensions { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/Md5HashHelper.cs b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/Md5HashHelper.cs new file mode 100644 index 000000000..c0b4965f1 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/Md5HashHelper.cs @@ -0,0 +1,34 @@ +using System.Security.Cryptography; +using System.Text; + +namespace Skoruba.IdentityServer4.STS.Identity.Helpers +{ + + /// + /// Helper-class to create Md5hashes from strings + /// + public static class Md5HashHelper + { + /// + /// Computes a Md5-hash of the submitted string and returns the corresponding hash + /// + /// + /// + public static string GetHash(string input) + { + using (var md5 = MD5.Create()) + { + var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); + + var sBuilder = new StringBuilder(); + + foreach (var dataByte in bytes) + { + sBuilder.Append(dataByte.ToString("x2")); + } + + return sBuilder.ToString(); + } + } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/SecurityHeadersAttribute.cs b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/SecurityHeadersAttribute.cs similarity index 97% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/SecurityHeadersAttribute.cs rename to src/Skoruba.IdentityServer4.STS.Identity/Helpers/SecurityHeadersAttribute.cs index e143c23be..780280685 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/SecurityHeadersAttribute.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/SecurityHeadersAttribute.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart +namespace Skoruba.IdentityServer4.STS.Identity.Helpers { public class SecurityHeadersAttribute : ActionFilterAttribute { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/StartupHelpers.cs b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/StartupHelpers.cs index 14b35ff73..3808f2c8d 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/StartupHelpers.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/StartupHelpers.cs @@ -1,11 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Globalization; +using System.Globalization; using System.Reflection; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Razor; @@ -14,8 +14,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using SendGrid; using Serilog; +using Skoruba.IdentityServer4.STS.Identity.Configuration; using Skoruba.IdentityServer4.STS.Identity.Configuration.Constants; +using Skoruba.IdentityServer4.STS.Identity.Services; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Skoruba.IdentityServer4.STS.Identity.Helpers @@ -60,9 +63,33 @@ public static void UseSecurityHeaders(this IApplicationBuilder app) app.UseReferrerPolicy(options => options.NoReferrer()); } + public static void AddEmailSenders(this IServiceCollection services, IConfiguration configuration) + { + var sendgridConnectionString = configuration.GetConnectionString(ConfigurationConsts.SendgridConnectionStringKey); + var smtpConfiguration = configuration.GetSection(nameof(SmtpConfiguration)).Get(); + var sendgridConfiguration = configuration.GetSection(nameof(SendgridConfiguration)).Get(); + + if (!string.IsNullOrWhiteSpace(sendgridConnectionString)) + { + services.AddSingleton(_ => new SendGridClient(sendgridConnectionString)); + services.AddSingleton(sendgridConfiguration); + services.AddTransient(); + } + else if (smtpConfiguration != null && !string.IsNullOrWhiteSpace(smtpConfiguration.Host)) + { + services.AddSingleton(smtpConfiguration); + services.AddTransient(); + } + else + { + services.AddSingleton(); + } + } + public static void AddAuthenticationServices(this IServiceCollection services, IHostingEnvironment hostingEnvironment, IConfiguration configuration, ILogger logger) where TContext : DbContext where TUserIdentity : class where TUserIdentityRole : class { + var connectionString = configuration.GetConnectionString(ConfigurationConsts.AdminConnectionStringKey); var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name; @@ -70,37 +97,67 @@ public static void AddAuthenticationServices() .AddDefaultTokenProviders(); + services.Configure(iis => { iis.AuthenticationDisplayName = "Windows"; iis.AutomaticAuthentication = false; }); + var authenticationBuilder = services.AddAuthentication(); + + AddExternalProviders(authenticationBuilder, configuration); + + AddIdentityServer(services, configuration, logger, connectionString, migrationsAssembly); + } + + private static void AddIdentityServer(IServiceCollection services, + IConfiguration configuration, ILogger logger, string connectionString, string migrationsAssembly) + where TContext : DbContext where TUserIdentity : class where TUserIdentityRole : class + { var builder = services.AddIdentityServer(options => - { - options.Events.RaiseErrorEvents = true; - options.Events.RaiseInformationEvents = true; - options.Events.RaiseFailureEvents = true; - options.Events.RaiseSuccessEvents = true; - }) + { + options.Events.RaiseErrorEvents = true; + options.Events.RaiseInformationEvents = true; + options.Events.RaiseFailureEvents = true; + options.Events.RaiseSuccessEvents = true; + }) .AddAspNetIdentity() .AddConfigurationStore(options => { - options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); + options.ConfigureDbContext = b => + b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); }) .AddOperationalStore(options => { - options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); + options.ConfigureDbContext = b => + b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); options.EnableTokenCleanup = true; #if DEBUG options.TokenCleanupInterval = 15; -#endif +#endif }); builder.AddCustomSigningCredential(configuration, logger); builder.AddCustomValidationKey(configuration, logger); } + private static void AddExternalProviders(AuthenticationBuilder authenticationBuilder, + IConfiguration configuration) + { + var externalProviderConfiguration = configuration.GetSection(nameof(ExternalProvidersConfiguration)).Get(); + + if (externalProviderConfiguration.UseGitHubProvider) + { + authenticationBuilder.AddGitHub(options => + { + options.ClientId = externalProviderConfiguration.GitHubClientId; + options.ClientSecret = externalProviderConfiguration.GitHubClientSecret; + options.Scope.Add("user:email"); + }); + } + } + public static void AddDbContexts(this IServiceCollection services, IConfiguration configuration) where TContext : DbContext { var connectionString = configuration.GetConnectionString(ConfigurationConsts.AdminConnectionStringKey); diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/TagHelpers/GravatarTagHelper.cs b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/TagHelpers/GravatarTagHelper.cs new file mode 100644 index 000000000..efa631b9d --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/TagHelpers/GravatarTagHelper.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace Skoruba.IdentityServer4.STS.Identity.Helpers.TagHelpers +{ + [HtmlTargetElement("img-gravatar")] + public class GravatarTagHelper : TagHelper + { + [HtmlAttributeName("email")] + public string Email { get; set; } + + [HtmlAttributeName("alt")] + public string Alt { get; set; } + + [HtmlAttributeName("class")] + public string Class { get; set; } + + [HtmlAttributeName("size")] + public int Size { get; set; } + + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (!string.IsNullOrWhiteSpace(Email)) + { + var hash = Md5HashHelper.GetHash(Email); + + output.TagName = "img"; + if (!string.IsNullOrWhiteSpace(Class)) + { + output.Attributes.Add("class", Class); + } + + if (!string.IsNullOrWhiteSpace(Alt)) + { + output.Attributes.Add("alt", Alt); + } + + output.Attributes.Add("src", GetAvatarUrl(hash, Size)); + output.TagMode = TagMode.SelfClosing; + } + } + + private static string GetAvatarUrl(string hash, int size) + { + var sizeArg = size > 0 ? $"?s={size}" : ""; + + return $"https://www.gravatar.com/avatar/{hash}{sizeArg}"; + } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/ExternalController.cs b/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/ExternalController.cs deleted file mode 100644 index 6dbf6bf95..000000000 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/ExternalController.cs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - -// Original file: https://github.com/IdentityServer/IdentityServer4.Samples -// Modified by Jan �koruba - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading.Tasks; -using IdentityModel; -using IdentityServer4.Events; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity; - -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account -{ - [SecurityHeaders] - [AllowAnonymous] - public class ExternalController : Controller - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly IEventService _events; - - public ExternalController( - UserManager userManager, - SignInManager signInManager, - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IEventService events) - { - _userManager = userManager; - _signInManager = signInManager; - _interaction = interaction; - _clientStore = clientStore; - _events = events; - } - - /// - /// initiate roundtrip to external authentication provider - /// - [HttpGet] - public async Task Challenge(string provider, string returnUrl) - { - if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; - - // validate returnUrl - either it is a valid OIDC URL or back to a local page - if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - - if (AccountOptions.WindowsAuthenticationSchemeName == provider) - { - // windows authentication needs special handling - return await ProcessWindowsLoginAsync(returnUrl); - } - else - { - // start challenge and roundtrip the return URL and scheme - var props = new AuthenticationProperties - { - RedirectUri = Url.Action(nameof(Callback)), - Items = - { - { "returnUrl", returnUrl }, - { "scheme", provider }, - } - }; - - return Challenge(props, provider); - } - } - - /// - /// Post processing of external authentication - /// - [HttpGet] - public async Task Callback() - { - // read external identity from the temporary cookie - var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme); - if (result?.Succeeded != true) - { - throw new Exception("External authentication error"); - } - - // lookup our user and external provider info - var (user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(result); - if (user == null) - { - // this might be where you might initiate a custom workflow for user registration - // in this sample we don't show how that would be done, as our sample implementation - // simply auto-provisions new external user - user = await AutoProvisionUserAsync(provider, providerUserId, claims); - } - - // this allows us to collect any additonal claims or properties - // for the specific prtotocols used and store them in the local auth cookie. - // this is typically used to store data needed for signout from those protocols. - var additionalLocalClaims = new List(); - var localSignInProps = new AuthenticationProperties(); - ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps); - ProcessLoginCallbackForWsFed(result, additionalLocalClaims, localSignInProps); - ProcessLoginCallbackForSaml2p(result, additionalLocalClaims, localSignInProps); - - // issue authentication cookie for user - // we must issue the cookie maually, and can't use the SignInManager because - // it doesn't expose an API to issue additional claims from the login workflow - var principal = await _signInManager.CreateUserPrincipalAsync(user); - additionalLocalClaims.AddRange(principal.Claims); - var name = principal.FindFirst(JwtClaimTypes.Name)?.Value ?? user.Id.ToString(); - await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id.ToString(), name)); - await HttpContext.SignInAsync(user.Id.ToString(), name, provider, localSignInProps, additionalLocalClaims.ToArray()); - - // delete temporary cookie used during external authentication - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - - // validate return URL and redirect back to authorization endpoint or a local page - var returnUrl = result.Properties.Items["returnUrl"]; - if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl)) - { - return Redirect(returnUrl); - } - - return Redirect("~/"); - } - - private async Task ProcessWindowsLoginAsync(string returnUrl) - { - // see if windows auth has already been requested and succeeded - var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName); - if (result?.Principal is WindowsPrincipal wp) - { - // we will issue the external cookie and then redirect the - // user back to the external callback, in essence, treating windows - // auth the same as any other external authentication mechanism - var props = new AuthenticationProperties() - { - RedirectUri = Url.Action("Callback"), - Items = - { - { "returnUrl", returnUrl }, - { "scheme", AccountOptions.WindowsAuthenticationSchemeName }, - } - }; - - var id = new ClaimsIdentity(AccountOptions.WindowsAuthenticationSchemeName); - id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name)); - id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name)); - - // add the groups as claims -- be careful if the number of groups is too large - if (AccountOptions.IncludeWindowsGroups) - { - var wi = wp.Identity as WindowsIdentity; - var groups = wi.Groups.Translate(typeof(NTAccount)); - var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value)); - id.AddClaims(roles); - } - - await HttpContext.SignInAsync( - global::IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme, - new ClaimsPrincipal(id), - props); - return Redirect(props.RedirectUri); - } - else - { - // trigger windows auth - // since windows auth don't support the redirect uri, - // this URL is re-triggered when we call challenge - return Challenge(AccountOptions.WindowsAuthenticationSchemeName); - } - } - - private async Task<(UserIdentity user, string provider, string providerUserId, IEnumerable claims)> - FindUserFromExternalProviderAsync(AuthenticateResult result) - { - var externalUser = result.Principal; - - // try to determine the unique id of the external user (issued by the provider) - // the most common claim type for that are the sub claim and the NameIdentifier - // depending on the external provider, some other claim type might be used - var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? - externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? - throw new Exception("Unknown userid"); - - // remove the user id claim so we don't include it as an extra claim if/when we provision the user - var claims = externalUser.Claims.ToList(); - claims.Remove(userIdClaim); - - var provider = result.Properties.Items["scheme"]; - var providerUserId = userIdClaim.Value; - - // find external user - var user = await _userManager.FindByLoginAsync(provider, providerUserId); - - return (user, provider, providerUserId, claims); - } - - private async Task AutoProvisionUserAsync(string provider, string providerUserId, IEnumerable claims) - { - // create a list of claims that we want to transfer into our store - var filtered = new List(); - - // user's display name - var name = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Name)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.Name)?.Value; - if (name != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, name)); - } - else - { - var first = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.GivenName)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.GivenName)?.Value; - var last = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.FamilyName)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.Surname)?.Value; - if (first != null && last != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, first + " " + last)); - } - else if (first != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, first)); - } - else if (last != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, last)); - } - } - - // email - var email = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.Email)?.Value; - if (email != null) - { - filtered.Add(new Claim(JwtClaimTypes.Email, email)); - } - - var user = new UserIdentity - { - UserName = Guid.NewGuid().ToString(), - }; - var identityResult = await _userManager.CreateAsync(user); - if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); - - if (filtered.Any()) - { - identityResult = await _userManager.AddClaimsAsync(user, filtered); - if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); - } - - identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider)); - if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); - - return user; - } - - - private void ProcessLoginCallbackForOidc(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) - { - // if the external system sent a session id claim, copy it over - // so we can use it for single sign-out - var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); - if (sid != null) - { - localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); - } - - // if the external provider issued an id_token, we'll keep it for signout - var id_token = externalResult.Properties.GetTokenValue("id_token"); - if (id_token != null) - { - localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); - } - } - - private void ProcessLoginCallbackForWsFed(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) - { - } - - private void ProcessLoginCallbackForSaml2p(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) - { - } - } -} \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Controllers/AccountController.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Controllers/AccountController.en.resx new file mode 100644 index 000000000..a9585dab3 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Controllers/AccountController.en.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The user does not exist or is not confirmed + + + Error from external provider: {0} + + + Invalid authenticator code. + + + Invalid recovery code entered. + + + Unable to load two-factor authentication user. + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Controllers/ManageController.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Controllers/ManageController.en.resx new file mode 100644 index 000000000..049281406 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Controllers/ManageController.en.resx @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + User with ID '{0}' asked for their personal data. + + + Your authenticator app has been verified. + + + User with ID '{0}' deleted themselves. + + + Cannot generate recovery codes for user with ID {0} because they do not have 2FA enabled. + + + Code + + + Unexpected error occurred deleting user with ID {0}. + + + Unexpected error occured disabling 2FA for user with ID {0}. + + + Cannot generate recovery codes for user as they do not have 2FA enabled. + + + Unexpected error occurred loading external login info for user with ID {0}. + + + Unexpected error occurred removing external login for user with ID {0}. + + + Unexpected error occurred setting email for user with ID {0}. + + + Unexpected error occurred setting phone number for user with ID {0}. + + + The external login was added. + + + The external login was removed. + + + Verification code is invalid. + + + Your password has been changed. + + + User {0} changed their password successfully. + + + Password not correct. + + + Your password has been set. + + + Your profile has been updated + + + User with ID {0} has disabled 2fa. + + + The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code. + + + User with id {0} has reset their authentication app key. + + + User with ID {0} has enabled 2FA with an authenticator app. + + + User with ID {0} has generated new 2FA recovery codes. + + + Unable to load user with ID {0}. + + + Verification email sent. Please check your email. + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ConfirmEmail.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ConfirmEmail.en.resx new file mode 100644 index 000000000..523a59ce7 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ConfirmEmail.en.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Thank you for confirming your email. + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ExternalLoginConfirmation.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ExternalLoginConfirmation.en.resx new file mode 100644 index 000000000..2a7d18db5 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ExternalLoginConfirmation.en.resx @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Email + + + You've successfully authenticated with {0}. + Please enter an email address for this site below and click the Register button to finish + logging in. + + + Register + + + Associate your {0} account. + + + Register + + + UserName + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ExternalLoginFailure.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ExternalLoginFailure.en.resx new file mode 100644 index 000000000..342c63467 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ExternalLoginFailure.en.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Unsuccessful login with service. + + + Login Failure + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ForgotPassword.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ForgotPassword.en.resx new file mode 100644 index 000000000..f3d6ddbb8 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ForgotPassword.en.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Submit + + + Enter your email. + + + Forgot your password? + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ForgotPasswordConfirmation.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ForgotPasswordConfirmation.en.resx new file mode 100644 index 000000000..e68eea659 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ForgotPasswordConfirmation.en.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Please check your email to reset your password. + + + Forgot password confirmation + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/Lockout.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/Lockout.en.resx new file mode 100644 index 000000000..50b7ece4b --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/Lockout.en.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + This account has been locked out, please try again later. + + + Locked out + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/Login.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/Login.en.resx index 229b87538..89e1293a6 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/Login.en.resx +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/Login.en.resx @@ -123,6 +123,9 @@ External Login + + Forgot password + Invalid login request diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/LoginWith2fa.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/LoginWith2fa.en.resx new file mode 100644 index 000000000..1113c2cd9 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/LoginWith2fa.en.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Authenticator code + + + Your login is protected with an authenticator app. Enter your authenticator code below. + + + Log in + + + log in with a recovery code + + + Don't have access to your authenticator device? You can + + + Remember this machine + + + Two-factor authentication + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/LoginWithRecoveryCode.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/LoginWithRecoveryCode.en.resx new file mode 100644 index 000000000..fe44702f1 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/LoginWithRecoveryCode.en.resx @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Recovery Code + + + You have requested to log in with a recovery code. This login will not be remembered until you provide + an authenticator app code at log in or disable 2FA and log in again. + + + Log in + + + Recovery code verification + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ResetPassword.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ResetPassword.en.resx new file mode 100644 index 000000000..51e9dcc43 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ResetPassword.en.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Confirm Password + + + Email + + + Password + + + Reset + + + Reset your password. + + + Reset password + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ResetPasswordConfirmation.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ResetPasswordConfirmation.en.resx new file mode 100644 index 000000000..8ec437229 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Account/ResetPasswordConfirmation.en.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Your password has been reset. Please + + + click here to log in + + + Reset password confirmation + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ChangePassword.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ChangePassword.en.resx new file mode 100644 index 000000000..9cb33bd67 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ChangePassword.en.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Confirm Password + + + New Password + + + Old Password + + + Change password + + + Update Password + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/DeletePersonalData.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/DeletePersonalData.en.resx new file mode 100644 index 000000000..3c58ac13f --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/DeletePersonalData.en.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Deleting this data will permanently remove your account, and this cannot be recovered. + + + Delete data and close my account + + + Password + + + Delete Personal Data + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/Disable2fa.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/Disable2fa.en.resx new file mode 100644 index 000000000..ed1d79d3a --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/Disable2fa.en.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Disable 2FA + + + Disabling 2FA does not change the keys used in authenticator apps. If you wish to change the key used in an authenticator app you should + + + reset your authenticator keys + + + This action only disables 2FA. + + + Disable two-factor authentication (2FA) + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/DownloadPersonalData.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/DownloadPersonalData.en.resx new file mode 100644 index 000000000..e30627773 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/DownloadPersonalData.en.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Download Your Data + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/EnableAuthenticator.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/EnableAuthenticator.en.resx new file mode 100644 index 000000000..d884da7ae --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/EnableAuthenticator.en.resx @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Verification Code + + + Download a two-factor authenticator app like Microsoft Authenticator for + + + Google Authenticator for + + + Once you have scanned the QR code or input the key above, your two factor authentication app will provide you + with a unique code. Enter the code in the confirmation box below. + + + Scan the QR Code or enter this key + + + into your two factor authenticator app. Spaces and casing do not matter. + + + To use an authenticator app go through the following steps: + + + Configure authenticator app + + + Verify + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ExternalLogins.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ExternalLogins.en.resx new file mode 100644 index 000000000..bd932c720 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ExternalLogins.en.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Remove + + + Registered Logins + + + Manage your external logins + + + Add another service to log in. + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/GenerateRecoveryCodes.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/GenerateRecoveryCodes.en.resx new file mode 100644 index 000000000..d14d724fa --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/GenerateRecoveryCodes.en.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Generate Recovery Codes + + + Generating new recovery codes does not change the keys used in authenticator apps. If you wish to change the key used in an authenticator app you should + + + If you lose your device and don't have the recovery codes you will lose access to your account. + + + reset your authenticator keys. + + + Put these codes in a safe place. + + + Generate two-factor authentication (2FA) recovery codes + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/Index.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/Index.en.resx new file mode 100644 index 000000000..6777407bf --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/Index.en.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Email + + + Phone Number + + + Save + + + Send verification email + + + Profile + + + UserName + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/LinkLoginFailure.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/LinkLoginFailure.en.resx new file mode 100644 index 000000000..342c63467 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/LinkLoginFailure.en.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Unsuccessful login with service. + + + Login Failure + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/PersonalData.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/PersonalData.en.resx new file mode 100644 index 000000000..e2ae84719 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/PersonalData.en.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Delete + + + Download + + + Your account contains personal data that you have given us. This page allows you to download or delete that data. + + + Personal Data + + + Deleting this data will permanently remove your account, and this cannot be recovered. + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ResetAuthenticator.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ResetAuthenticator.en.resx new file mode 100644 index 000000000..e2bdee855 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ResetAuthenticator.en.resx @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + This process disables 2FA until you verify your authenticator app. + If you do not complete your authenticator app configuration you may lose access to your account. + + + Reset authenticator key + + + If you reset your authenticator key your authenticator app will not work until you reconfigure it. + + + Reset authenticator key + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/SetPassword.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/SetPassword.en.resx new file mode 100644 index 000000000..76c64dde9 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/SetPassword.en.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Confirm Password + + + You do not have a local username/password for this site. Add a local account so you can log in without an external login. + + + New Password + + + Set Password + + + Set your password + + + Set password + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ShowRecoveryCodes.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ShowRecoveryCodes.en.resx new file mode 100644 index 000000000..bc55c1c3a --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/ShowRecoveryCodes.en.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + If you lose your device and don't have the recovery codes you will lose access to your account. + + + Put these codes in a safe place. + + + Recovery codes + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/TwoFactorAuthentication.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/TwoFactorAuthentication.en.resx new file mode 100644 index 000000000..9f7407269 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Manage/TwoFactorAuthentication.en.resx @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Add authenticator app + + + Authenticator app + + + before you can log in with a recovery code + + + Disable 2FA + + + Forget this browser + + + generate a new set of recovery codes + + + You have no recovery codes left + + + You have 1 recovery code left + + + recovery codes left + + + Reset authenticator app + + + Reset recovery codes + + + Setup authenticator app + + + Two-factor authentication (2FA) + + + You can generate a new set of recovery codes + + + You have + + + You must + + + You should + + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Shared/_Layout.en.resx b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Shared/_Layout.en.resx index efcacfc8f..72eb00fb3 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Shared/_Layout.en.resx +++ b/src/Skoruba.IdentityServer4.STS.Identity/Resources/Views/Shared/_Layout.en.resx @@ -117,6 +117,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Two-factor authentication + + + Change password + + + My external logins + Skoruba IdentityServer4 @@ -126,6 +135,12 @@ Grants + + My personal data + + + My profile + Menu diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Services/EmailSender.cs b/src/Skoruba.IdentityServer4.STS.Identity/Services/EmailSender.cs new file mode 100644 index 000000000..fdfeb995b --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Services/EmailSender.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.Extensions.Logging; + +namespace Skoruba.IdentityServer4.STS.Identity.Services +{ + public class EmailSender : IEmailSender + { + private readonly ILogger _logger; + + public EmailSender(ILogger logger) + { + _logger = logger; + } + + public Task SendEmailAsync(string email, string subject, string htmlMessage) + { + _logger.LogInformation($"Email: {email}, subject: {subject}, message: {htmlMessage}"); + + return Task.FromResult(0); + } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Services/SendgridEmailSender.cs b/src/Skoruba.IdentityServer4.STS.Identity/Services/SendgridEmailSender.cs new file mode 100644 index 000000000..19a04fe72 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Services/SendgridEmailSender.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.Extensions.Logging; +using SendGrid; +using Skoruba.IdentityServer4.STS.Identity.Configuration; +using System.Threading.Tasks; + +namespace Skoruba.IdentityServer4.STS.Identity.Services +{ + public class SendgridEmailSender : IEmailSender + { + private ISendGridClient _client; + private readonly SendgridConfiguration _configuration; + private readonly ILogger _logger; + + public SendgridEmailSender(ILogger logger, ISendGridClient client, SendgridConfiguration configuration) + { + _logger = logger; + _client = client; + _configuration = configuration; + } + public async Task SendEmailAsync(string email, string subject, string htmlMessage) + { + var msg = SendGrid.Helpers.Mail.MailHelper.CreateSingleEmail( + new SendGrid.Helpers.Mail.EmailAddress(_configuration.SourceEmail, _configuration.SourceName), + new SendGrid.Helpers.Mail.EmailAddress(email), + subject, + null, + htmlMessage + ); + + var response = await _client.SendEmailAsync(msg); + if ((response.StatusCode == System.Net.HttpStatusCode.OK) || (response.StatusCode == System.Net.HttpStatusCode.Created)) + { + _logger.LogInformation($"Email: {email}, subject: {subject}, message: {htmlMessage} successfully sent"); + } else + { + var errorMessage = response.Body.ReadAsStringAsync(); + _logger.LogError($"Response with code {response.StatusCode} and body {errorMessage} after sending email: {email}, subject: {subject}"); + } + } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Services/SmtpEmailSender.cs b/src/Skoruba.IdentityServer4.STS.Identity/Services/SmtpEmailSender.cs new file mode 100644 index 000000000..70fb2f20d --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Services/SmtpEmailSender.cs @@ -0,0 +1,49 @@ +using System.Net.Mail; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.Extensions.Logging; +using Skoruba.IdentityServer4.STS.Identity.Configuration; + +namespace Skoruba.IdentityServer4.STS.Identity.Services +{ + public class SmtpEmailSender : IEmailSender + { + private readonly ILogger _logger; + private readonly SmtpConfiguration _configuration; + private readonly SmtpClient _client; + + public SmtpEmailSender(SmtpConfiguration configuration, ILogger logger) + { + _logger = logger; + _configuration = configuration; + _client = new SmtpClient + { + Host = _configuration.Host, + Port = _configuration.Port, + DeliveryMethod = SmtpDeliveryMethod.Network, + EnableSsl = _configuration.UseSSL, + Credentials = new System.Net.NetworkCredential(_configuration.Login, _configuration.Password) + }; + } + + public Task SendEmailAsync(string email, string subject, string htmlMessage) + { + _logger.LogInformation($"Sending email: {email}, subject: {subject}, message: {htmlMessage}"); + try + { + var mail = new MailMessage(_configuration.Login, email); + mail.IsBodyHtml = true; + mail.Subject = subject; + mail.Body = htmlMessage; + _client.Send(mail); + _logger.LogInformation($"Email: {email}, subject: {subject}, message: {htmlMessage} successfully sent"); + return Task.CompletedTask; + } + catch (SmtpException ex) + { + _logger.LogError($"Exception {ex} during sending email: {email}, subject: {subject}"); + throw; + } + } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Skoruba.IdentityServer4.STS.Identity.csproj b/src/Skoruba.IdentityServer4.STS.Identity/Skoruba.IdentityServer4.STS.Identity.csproj index 48faaefdc..30fdb8082 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Skoruba.IdentityServer4.STS.Identity.csproj +++ b/src/Skoruba.IdentityServer4.STS.Identity/Skoruba.IdentityServer4.STS.Identity.csproj @@ -6,11 +6,13 @@ + + diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs b/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs index 0e9ee5c2a..08206f04f 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs @@ -36,6 +36,7 @@ public Startup(IHostingEnvironment environment, ILoggerFactory loggerFactory) public void ConfigureServices(IServiceCollection services) { services.AddDbContexts(Configuration); + services.AddEmailSenders(Configuration); services.AddAuthenticationServices(Environment, Configuration, Logger); services.AddMvcLocalization(); } diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ExternalLoginConfirmationViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ExternalLoginConfirmationViewModel.cs new file mode 100644 index 000000000..39bd6ffb8 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ExternalLoginConfirmationViewModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account +{ + public class ExternalLoginConfirmationViewModel + { + [Required] + [RegularExpression(@"^[a-zA-Z0-9_@\-\.\+]+$")] + public string UserName { get; set; } + + [Required] + [EmailAddress] + public string Email { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/ExternalProvider.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ExternalProvider.cs similarity index 87% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/ExternalProvider.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ExternalProvider.cs index 647303dff..46c2c1f2b 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/ExternalProvider.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ExternalProvider.cs @@ -4,7 +4,7 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Samples // Modified by Jan Škoruba -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account { public class ExternalProvider { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ForgotPasswordViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ForgotPasswordViewModel.cs new file mode 100644 index 000000000..66fecfbae --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ForgotPasswordViewModel.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account +{ + public class ForgotPasswordViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LoggedOutViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoggedOutViewModel.cs similarity index 92% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LoggedOutViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoggedOutViewModel.cs index d00c95cc8..7fc2d600c 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LoggedOutViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoggedOutViewModel.cs @@ -4,7 +4,7 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI // Modified by Jan Škoruba -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account { public class LoggedOutViewModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LoginInputModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginInputModel.cs similarity index 90% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LoginInputModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginInputModel.cs index b5bf4e01c..8f64052e6 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LoginInputModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginInputModel.cs @@ -6,7 +6,7 @@ using System.ComponentModel.DataAnnotations; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account { public class LoginInputModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LoginViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginViewModel.cs similarity index 94% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LoginViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginViewModel.cs index dbc69a9e9..1b9b776fa 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LoginViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginViewModel.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; using System.Linq; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account { public class LoginViewModel : LoginInputModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginWith2faViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginWith2faViewModel.cs new file mode 100644 index 000000000..5d295e227 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginWith2faViewModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account +{ + public class LoginWith2faViewModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + public string TwoFactorCode { get; set; } + public bool RememberMachine { get; set; } + public bool RememberMe { get; set; } + public string ReturnUrl { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginWithRecoveryCodeViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginWithRecoveryCodeViewModel.cs new file mode 100644 index 000000000..e9d4d1f5a --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LoginWithRecoveryCodeViewModel.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account +{ + public class LoginWithRecoveryCodeViewModel + { + [Required] + [DataType(DataType.Text)] + public string RecoveryCode { get; set; } + + public string ReturnUrl { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LogoutInputModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LogoutInputModel.cs similarity index 85% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LogoutInputModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LogoutInputModel.cs index dfe8c80be..4a8bb612e 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LogoutInputModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LogoutInputModel.cs @@ -4,7 +4,7 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI // Modified by Jan Škoruba -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account { public class LogoutInputModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LogoutViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LogoutViewModel.cs similarity index 86% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LogoutViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LogoutViewModel.cs index 8b6307184..c83263c01 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/LogoutViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/LogoutViewModel.cs @@ -4,7 +4,7 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI // Modified by Jan Škoruba -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account { public class LogoutViewModel : LogoutInputModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/RedirectViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/RedirectViewModel.cs similarity index 85% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/RedirectViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/RedirectViewModel.cs index aa0af4a37..f9acc3cf8 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Account/RedirectViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/RedirectViewModel.cs @@ -4,7 +4,7 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI // Modified by Jan �koruba -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Account +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account { public class RedirectViewModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ResetPasswordViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ResetPasswordViewModel.cs new file mode 100644 index 000000000..cd39c757d --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Account/ResetPasswordViewModel.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Account +{ + public class ResetPasswordViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Compare("Password")] + public string ConfirmPassword { get; set; } + + public string Code { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentInputModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ConsentInputModel.cs similarity index 90% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentInputModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ConsentInputModel.cs index f2a838574..939687d35 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentInputModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ConsentInputModel.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Consent { public class ConsentInputModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ConsentViewModel.cs similarity index 92% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ConsentViewModel.cs index 6408db722..b43ad0824 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ConsentViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ConsentViewModel.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Consent { public class ConsentViewModel : ConsentInputModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ProcessConsentResult.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ProcessConsentResult.cs similarity index 91% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ProcessConsentResult.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ProcessConsentResult.cs index b03e172f4..18c5879b9 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ProcessConsentResult.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ProcessConsentResult.cs @@ -4,7 +4,7 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI // Modified by Jan �koruba -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Consent { public class ProcessConsentResult { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ScopeViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ScopeViewModel.cs similarity index 90% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ScopeViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ScopeViewModel.cs index 6eed2c907..133a4e3aa 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Consent/ScopeViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Consent/ScopeViewModel.cs @@ -4,7 +4,7 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI // Modified by Jan Škoruba -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Consent { public class ScopeViewModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Device/DeviceAuthorizationInputModel.cs similarity index 76% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Device/DeviceAuthorizationInputModel.cs index 25173d2dc..e5c7d2b23 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Device/DeviceAuthorizationInputModel.cs @@ -4,9 +4,9 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI // Modified by Jan �koruba -using Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Consent; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Device +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Device { public class DeviceAuthorizationInputModel : ConsentInputModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Device/DeviceAuthorizationViewModel.cs similarity index 78% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Device/DeviceAuthorizationViewModel.cs index 0cdb71517..0084583ee 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Device/DeviceAuthorizationViewModel.cs @@ -4,9 +4,9 @@ // Original file: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI // Modified by Jan �koruba -using Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent; +using Skoruba.IdentityServer4.STS.Identity.ViewModels.Consent; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Device +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Device { public class DeviceAuthorizationViewModel : ConsentViewModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Diagnostics/DiagnosticsViewModel.cs similarity index 94% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Diagnostics/DiagnosticsViewModel.cs index 927d5e90f..e795c5c9f 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Diagnostics/DiagnosticsViewModel.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Authentication; using Newtonsoft.Json; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Diagnostics +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Diagnostics { public class DiagnosticsViewModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Grants/GrantsViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Grants/GrantsViewModel.cs similarity index 93% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Grants/GrantsViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Grants/GrantsViewModel.cs index 93aa59a53..465a4721f 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Grants/GrantsViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Grants/GrantsViewModel.cs @@ -7,7 +7,7 @@ using System; using System.Collections.Generic; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Grants +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Grants { public class GrantsViewModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Home/ErrorViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Home/ErrorViewModel.cs similarity index 86% rename from src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Home/ErrorViewModel.cs rename to src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Home/ErrorViewModel.cs index 0904c15bd..0a4a45500 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Quickstart/Home/ErrorViewModel.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Home/ErrorViewModel.cs @@ -6,7 +6,7 @@ using IdentityServer4.Models; -namespace Skoruba.IdentityServer4.STS.Identity.Quickstart.Home +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Home { public class ErrorViewModel { diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/ChangePasswordViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/ChangePasswordViewModel.cs new file mode 100644 index 000000000..f03ddf1b8 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/ChangePasswordViewModel.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage +{ + public class ChangePasswordViewModel + { + [Required] + [DataType(DataType.Password)] + public string OldPassword { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Compare("NewPassword")] + public string ConfirmPassword { get; set; } + + public string StatusMessage { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/DeletePersonalDataViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/DeletePersonalDataViewModel.cs new file mode 100644 index 000000000..3e1f16f8a --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/DeletePersonalDataViewModel.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage +{ + public class DeletePersonalDataViewModel + { + public bool RequirePassword { get; set; } + + [DataType(DataType.Password)] + [Required] + public string Password { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/EnableAuthenticatorViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/EnableAuthenticatorViewModel.cs new file mode 100644 index 000000000..a14010e65 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/EnableAuthenticatorViewModel.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage +{ + public class EnableAuthenticatorViewModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Verification Code")] + public string Code { get; set; } + + [BindNever] + public string SharedKey { get; set; } + + [BindNever] + public string AuthenticatorUri { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/ExternalLoginsViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/ExternalLoginsViewModel.cs new file mode 100644 index 000000000..2709d372e --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/ExternalLoginsViewModel.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage +{ + public class ExternalLoginsViewModel + { + public IList CurrentLogins { get; set; } + + public IList OtherLogins { get; set; } + + public bool ShowRemoveButton { get; set; } + + public string StatusMessage { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/IndexViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/IndexViewModel.cs new file mode 100644 index 000000000..3e6ec0c73 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/IndexViewModel.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage +{ + public class IndexViewModel + { + public string Username { get; set; } + + public bool IsEmailConfirmed { get; set; } + + [Required] + [EmailAddress] + public string Email { get; set; } + + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + + public string StatusMessage { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/RemoveLoginViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/RemoveLoginViewModel.cs new file mode 100644 index 000000000..1f00983b8 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/RemoveLoginViewModel.cs @@ -0,0 +1,8 @@ +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage +{ + public class RemoveLoginViewModel + { + public string LoginProvider { get; set; } + public string ProviderKey { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/SetPasswordViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/SetPasswordViewModel.cs new file mode 100644 index 000000000..94538f0c6 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/SetPasswordViewModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage +{ + public class SetPasswordViewModel + { + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Compare("NewPassword")] + public string ConfirmPassword { get; set; } + + public string StatusMessage { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/ShowRecoveryCodesViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/ShowRecoveryCodesViewModel.cs new file mode 100644 index 000000000..05f5ade19 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/ShowRecoveryCodesViewModel.cs @@ -0,0 +1,7 @@ +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage +{ + public class ShowRecoveryCodesViewModel + { + public string[] RecoveryCodes { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs new file mode 100644 index 000000000..126dfa2e8 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs @@ -0,0 +1,13 @@ +namespace Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage +{ + public class TwoFactorAuthenticationViewModel + { + public bool HasAuthenticator { get; set; } + + public int RecoveryCodesLeft { get; set; } + + public bool Is2faEnabled { get; set; } + + public bool IsMachineRemembered { get; set; } + } +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ConfirmEmail.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ConfirmEmail.cshtml new file mode 100644 index 000000000..3c780dcbe --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ConfirmEmail.cshtml @@ -0,0 +1,12 @@ +@using Microsoft.AspNetCore.Mvc.Localization +@inject IViewLocalizer Localizer +@{ + ViewData["Title"] = "Confirm email"; +} + +

    @ViewData["Title"]

    +
    +

    + @Localizer["ConfirmMessage"] +

    +
    diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ExternalLoginConfirmation.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ExternalLoginConfirmation.cshtml new file mode 100644 index 000000000..dd82aaf5f --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ExternalLoginConfirmation.cshtml @@ -0,0 +1,36 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Account.ExternalLoginConfirmationViewModel +@{ + ViewData["Title"] = Localizer["Title"]; +} + +

    @ViewData["Title"]

    + +

    @Localizer["SubTitle", ViewData["LoginProvider"].ToString()]

    + +
    +
    + +
    + +
    + + +
    +
    +
    + +
    + + +
    +
    +
    +
    + +
    +
    + \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ExternalLoginFailure.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ExternalLoginFailure.cshtml new file mode 100644 index 000000000..b6a244c8c --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ExternalLoginFailure.cshtml @@ -0,0 +1,10 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@{ + ViewData["Title"] = Localizer["Title"]; +} + +
    +

    @ViewData["Title"].

    +

    @Localizer["Error"]

    +
    \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ForgotPassword.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ForgotPassword.cshtml new file mode 100644 index 000000000..df331662f --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ForgotPassword.cshtml @@ -0,0 +1,25 @@ +@using Microsoft.AspNetCore.Mvc.Localization +@inject IViewLocalizer Localizer +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Account.ForgotPasswordViewModel +@{ + ViewData["Title"] = Localizer["Title"]; +} + +@await Html.PartialAsync("_ValidationSummary") + +

    @ViewData["Title"]

    +

    @Localizer["SubTitle"]

    + +
    +
    +
    +
    +
    + + + +
    + + +
    +
    \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ForgotPasswordConfirmation.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ForgotPasswordConfirmation.cshtml new file mode 100644 index 000000000..cb29ca3ce --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/ForgotPasswordConfirmation.cshtml @@ -0,0 +1,12 @@ +@using Microsoft.AspNetCore.Mvc.Localization +@inject IViewLocalizer Localizer +@{ + ViewData["Title"] = Localizer["Title"]; +} + +

    @ViewData["Title"]

    + + + diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/Lockout.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/Lockout.cshtml new file mode 100644 index 000000000..671175e9c --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/Lockout.cshtml @@ -0,0 +1,10 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@{ + ViewData["Title"] = Localizer["Title"]; +} + +
    +

    @ViewData["Title"]

    +

    @Localizer["Info"]

    +
    \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/LoggedOut.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/LoggedOut.cshtml index 197233124..6df8e74c3 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/LoggedOut.cshtml +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/LoggedOut.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @inject IViewLocalizer Localizer -@model Skoruba.IdentityServer4.STS.Identity.Quickstart.Account.LoggedOutViewModel +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Account.LoggedOutViewModel @{ // set this so the layout rendering sees an anonymous user diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/Login.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/Login.cshtml index 8290cc7fe..9c56e1a81 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/Login.cshtml +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Account/Login.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @inject IViewLocalizer Localizer -@model Skoruba.IdentityServer4.STS.Identity.Quickstart.Account.LoginViewModel +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Account.LoginViewModel
    ").addClass("cw").text("#"));t.isBefore(this._viewDate.clone().endOf("w"));)e.append(k("").addClass("dow").text(t.format("dd"))),t.add(1,"d");this.widget.find(".datepicker-days thead").append(e)},r.prototype._fillMonths=function(){for(var e=[],t=this._viewDate.clone().startOf("y").startOf("d");t.isSame(this._viewDate,"y");)e.push(k("").attr("data-action","selectMonth").addClass("month").text(t.format("MMM"))),t.add(1,"M");this.widget.find(".datepicker-months td").empty().append(e)},r.prototype._updateMonths=function(){var e=this.widget.find(".datepicker-months"),t=e.find("th"),n=e.find("tbody").find("span"),i=this;t.eq(0).find("span").attr("title",this._options.tooltips.prevYear),t.eq(1).attr("title",this._options.tooltips.selectYear),t.eq(2).find("span").attr("title",this._options.tooltips.nextYear),e.find(".disabled").removeClass("disabled"),this._isValid(this._viewDate.clone().subtract(1,"y"),"y")||t.eq(0).addClass("disabled"),t.eq(1).text(this._viewDate.year()),this._isValid(this._viewDate.clone().add(1,"y"),"y")||t.eq(2).addClass("disabled"),n.removeClass("active"),this._getLastPickedDate().isSame(this._viewDate,"y")&&!this.unset&&n.eq(this._getLastPickedDate().month()).addClass("active"),n.each(function(e){i._isValid(i._viewDate.clone().month(e),"M")||k(this).addClass("disabled")})},r.prototype._getStartEndYear=function(e,t){var n=e/10,i=Math.floor(t/e)*e;return[i,i+9*n,Math.floor(t/n)*n]},r.prototype._updateYears=function(){var e=this.widget.find(".datepicker-years"),t=e.find("th"),n=this._getStartEndYear(10,this._viewDate.year()),i=this._viewDate.clone().year(n[0]),r=this._viewDate.clone().year(n[1]),o="";for(t.eq(0).find("span").attr("title",this._options.tooltips.prevDecade),t.eq(1).attr("title",this._options.tooltips.selectDecade),t.eq(2).find("span").attr("title",this._options.tooltips.nextDecade),e.find(".disabled").removeClass("disabled"),this._options.minDate&&this._options.minDate.isAfter(i,"y")&&t.eq(0).addClass("disabled"),t.eq(1).text(i.year()+"-"+r.year()),this._options.maxDate&&this._options.maxDate.isBefore(r,"y")&&t.eq(2).addClass("disabled"),o+=''+(i.year()-1)+"";!i.isAfter(r,"y");)o+=''+i.year()+"",i.add(1,"y");o+=''+i.year()+"",e.find("td").html(o)},r.prototype._updateDecades=function(){var e=this.widget.find(".datepicker-decades"),t=e.find("th"),n=this._getStartEndYear(100,this._viewDate.year()),i=this._viewDate.clone().year(n[0]),r=this._viewDate.clone().year(n[1]),o=!1,a=!1,s=void 0,l="";for(t.eq(0).find("span").attr("title",this._options.tooltips.prevCentury),t.eq(2).find("span").attr("title",this._options.tooltips.nextCentury),e.find(".disabled").removeClass("disabled"),(0===i.year()||this._options.minDate&&this._options.minDate.isAfter(i,"y"))&&t.eq(0).addClass("disabled"),t.eq(1).text(i.year()+"-"+r.year()),this._options.maxDate&&this._options.maxDate.isBefore(r,"y")&&t.eq(2).addClass("disabled"),i.year()-10<0?l+=" ":l+=''+(i.year()-10)+"";!i.isAfter(r,"y");)s=i.year()+11,o=this._options.minDate&&this._options.minDate.isAfter(i,"y")&&this._options.minDate.year()<=s,a=this._options.maxDate&&this._options.maxDate.isAfter(i,"y")&&this._options.maxDate.year()<=s,l+=''+i.year()+"",i.add(10,"y");l+=''+i.year()+"",e.find("td").html(l)},r.prototype._fillDate=function(){var e=this.widget.find(".datepicker-days"),t=e.find("th"),n=[],i=void 0,r=void 0,o=void 0,a=void 0;if(this._hasDate()){for(t.eq(0).find("span").attr("title",this._options.tooltips.prevMonth),t.eq(1).attr("title",this._options.tooltips.selectMonth),t.eq(2).find("span").attr("title",this._options.tooltips.nextMonth),e.find(".disabled").removeClass("disabled"),t.eq(1).text(this._viewDate.format(this._options.dayViewHeaderFormat)),this._isValid(this._viewDate.clone().subtract(1,"M"),"M")||t.eq(0).addClass("disabled"),this._isValid(this._viewDate.clone().add(1,"M"),"M")||t.eq(2).addClass("disabled"),i=this._viewDate.clone().startOf("M").startOf("w").startOf("d"),a=0;a<42;a++){if(0===i.weekday()&&(r=k("
    '+i.week()+"'+i.date()+"
    '+t.format(this.use24Hours?"HH":"hh")+"
    '+t.format("mm")+"
    '+t.format("ss")+"
    + + @foreach (var login in Model.CurrentLogins) + { + + + + + } + +
    @login.LoginProvider + @if (Model.ShowRemoveButton) + { +
    +
    + + + +
    +
    + } + else + { + @:   + } +
    +} +@if (Model.OtherLogins?.Count > 0) +{ +

    @Localizer["TitleAddAnotherService"]

    +
    +
    +
    +

    + @foreach (var provider in Model.OtherLogins) + { + + } +

    +
    +
    +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/GenerateRecoveryCodes.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/GenerateRecoveryCodes.cshtml new file mode 100644 index 000000000..c06e0fbf8 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/GenerateRecoveryCodes.cshtml @@ -0,0 +1,26 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@{ + ViewData["Title"] = Localizer["Title"]; +} + +

    @ViewData["Title"]

    + + + +
    +
    + +
    +
    diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/Index.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/Index.cshtml new file mode 100644 index 000000000..2aacece58 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/Index.cshtml @@ -0,0 +1,45 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage.IndexViewModel + +

    @Localizer["Title"]

    + +@await Html.PartialAsync("_ValidationSummary") + +@await Html.PartialAsync("_StatusMessage", Model.StatusMessage) + +
    +
    + +
    +
    +
    +
    + + +
    +
    + + + @if (Model.IsEmailConfirmed) + { +
    + +
    + } + else + { + + + } + +
    +
    + + + +
    + +
    +
    +
    \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/LinkLoginFailure.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/LinkLoginFailure.cshtml new file mode 100644 index 000000000..cf01e2e8e --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/LinkLoginFailure.cshtml @@ -0,0 +1,12 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@{ + ViewData["Title"] = Localizer["Title"]; +} + +@await Html.PartialAsync("_ValidationSummary") + +
    +

    @ViewData["Title"]

    +

    @Localizer["Error"]

    +
    \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/PersonalData.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/PersonalData.cshtml new file mode 100644 index 000000000..c7b41a563 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/PersonalData.cshtml @@ -0,0 +1,25 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization + +@{ + ViewData["Title"] = Localizer["Title"]; +} + +

    @ViewData["Title"]

    + +
    +
    + +
    + +
    + + +
    +
    \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/ResetAuthenticator.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/ResetAuthenticator.cshtml new file mode 100644 index 000000000..1b8fa75d9 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/ResetAuthenticator.cshtml @@ -0,0 +1,21 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@{ + ViewData["Title"] = Localizer["Title"]; +} + +

    @ViewData["Title"]

    + +
    +
    + +
    +
    \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/SetPassword.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/SetPassword.cshtml new file mode 100644 index 000000000..2f34f72ed --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/SetPassword.cshtml @@ -0,0 +1,31 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage.SetPasswordViewModel +@{ + ViewData["Title"] = Localizer["Title"]; +} + +

    @Localizer["SubTitle"]

    +@await Html.PartialAsync("_StatusMessage", Model.StatusMessage) + + +
    +
    +
    +
    +
    + + + +
    +
    + + + +
    + +
    +
    +
    diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/ShowRecoveryCodes.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/ShowRecoveryCodes.cshtml new file mode 100644 index 000000000..9a467a740 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/ShowRecoveryCodes.cshtml @@ -0,0 +1,25 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage.ShowRecoveryCodesViewModel +@{ + ViewData["Title"] = Localizer["Title"]; +} + +

    @ViewData["Title"]

    + +
    +
    + @for (var row = 0; row < Model.RecoveryCodes.Length; row += 2) + { + @Model.RecoveryCodes[row] @Model.RecoveryCodes[row + 1]
    + } +
    +
    \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/TwoFactorAuthentication.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/TwoFactorAuthentication.cshtml new file mode 100644 index 000000000..7ec83aa6d --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/TwoFactorAuthentication.cshtml @@ -0,0 +1,78 @@ +@inject IViewLocalizer Localizer +@using Microsoft.AspNetCore.Mvc.Localization +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Manage.TwoFactorAuthenticationViewModel +@{ + ViewData["Title"] = Localizer["Title"]; +} + +
    + +
    +

    @ViewData["Title"]

    +
    + + @if (Model.Is2faEnabled) + { + if (Model.RecoveryCodesLeft == 0) + { +
    +
    + @Localizer["NoCodes"] +

    @Localizer["YouMust"] @Localizer["GenerateNewCodes"] @Localizer["BeforeLogin"]

    +
    +
    + } + else if (Model.RecoveryCodesLeft == 1) + { +
    +
    + @Localizer["OneCode"] +

    @Localizer["YouCanGenerateCodes"] @Localizer["GenerateNewCodes"]

    +
    +
    + } + else if (Model.RecoveryCodesLeft <= 3) + { +
    +
    + @Localizer["YouHave"] @Model.RecoveryCodesLeft.ToString() @Localizer["RecoveryCodeLeft"] +

    @Localizer["YouShould"] @Localizer["GenerateNewCodes"]

    +
    +
    + } + + if (Model.IsMachineRemembered) + { +
    +
    + +
    +
    + } + + + } + +
    + +
    + +
    +

    @Localizer["AuthenticatorApp"]

    +
    + +
    + @if (!Model.HasAuthenticator) + { + @Localizer["AddAuthenticator"] + } + else + { + @Localizer["SetupAuthenticator"] + @Localizer["ResetAuthenticator"] + } +
    +
    diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/_StatusMessage.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/_StatusMessage.cshtml new file mode 100644 index 000000000..092bf1741 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Manage/_StatusMessage.cshtml @@ -0,0 +1,10 @@ +@model string + +@if (!String.IsNullOrEmpty(Model)) +{ + var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; + +} diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/Error.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/Error.cshtml index 2fc106221..4a587e8cb 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/Error.cshtml +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/Error.cshtml @@ -1,7 +1,7 @@ @using Microsoft.AspNetCore.Mvc.Localization @inject IViewLocalizer Localizer @using Microsoft.AspNetCore.Hosting -@model Skoruba.IdentityServer4.STS.Identity.Quickstart.Home.ErrorViewModel +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Home.ErrorViewModel @inject IHostingEnvironment host @{ diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/Redirect.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/Redirect.cshtml index b2846ad35..e819a2a4a 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/Redirect.cshtml +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/Redirect.cshtml @@ -1,4 +1,4 @@ -@model Skoruba.IdentityServer4.STS.Identity.Quickstart.Account.RedirectViewModel +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Account.RedirectViewModel @using Microsoft.AspNetCore.Mvc.Localization @inject IViewLocalizer Localizer

    @Localizer["Title"]

    diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/_Layout.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/_Layout.cshtml index c20122aed..17a1bb88e 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/_Layout.cshtml +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/_Layout.cshtml @@ -1,85 +1,101 @@ @using IdentityServer4.Extensions +@using Microsoft.AspNetCore.Identity @using Microsoft.AspNetCore.Mvc.Localization +@using Skoruba.IdentityServer4.Admin.EntityFramework.Identity.Entities.Identity @inject IViewLocalizer Localizer @{ - string name = null; - if (!true.Equals(ViewData["signed-out"])) - { - name = Context.User?.GetDisplayName(); - } + string name = null; + if (!true.Equals(ViewData["signed-out"])) + { + name = Context.User?.GetDisplayName(); + } +} + +@inject SignInManager SignInManager +@{ + var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any(); } - - - - @Localizer["PageTitle"] - - + + + + @Localizer["PageTitle"] + + - - - - - - - - + + + + + + + + - @RenderSection("styles", required: false) + @RenderSection("styles", required: false) - -
    - @RenderBody() +
    + @RenderBody() -
    -
    -
    - @Localizer["Footer"] - @Localizer["FooterCopyright"] -
    -
    - @await Html.PartialAsync("Common/SelectLanguage") -
    -
    -
    -
    +
    +
    +
    + @Localizer["Footer"] + @Localizer["FooterCopyright"] +
    +
    + @await Html.PartialAsync("Common/SelectLanguage") +
    +
    +
    +
    - - - - - - + + + + + + - @RenderSection("scripts", required: false) + @RenderSection("scripts", required: false) diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/_ScopeListItem.cshtml b/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/_ScopeListItem.cshtml index 7093eeff6..8d1168422 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/_ScopeListItem.cshtml +++ b/src/Skoruba.IdentityServer4.STS.Identity/Views/Shared/_ScopeListItem.cshtml @@ -1,6 +1,6 @@ @using Microsoft.AspNetCore.Mvc.Localization @inject IViewLocalizer Localizer -@model Skoruba.IdentityServer4.STS.Identity.Quickstart.Consent.ScopeViewModel +@model Skoruba.IdentityServer4.STS.Identity.ViewModels.Consent.ScopeViewModel
  • diff --git a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json index dbde81225..194d612e9 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json +++ b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json @@ -1,46 +1,56 @@ { - "ConnectionStrings": { - "AdminConnection": "Server=(localdb)\\mssqllocaldb;Database=IdentityServer4Admin;Trusted_Connection=True;MultipleActiveResultSets=true" - }, - "Serilog": { - "MinimumLevel": "Error", - "WriteTo": [ - { - "Name": "File", - "Args": { - "path": "Log\\skoruba_admin.txt", - "rollingInterval": "Day" - } - }, - { - "Name": "MSSqlServer", - "Args": { - "connectionString": "Server=(localdb)\\mssqllocaldb;Database=IdentityServer4Admin;Trusted_Connection=True;MultipleActiveResultSets=true", - "tableName": "Log", - "columnOptionsSection": { - "addStandardColumns": [ "LogEvent" ], - "removeStandardColumns": [ "Properties" ] - } - } - } - ] - }, - "CertificateConfiguration": { + "ConnectionStrings": { + "AdminConnection": "Server=(localdb)\\mssqllocaldb;Database=IdentityServer4Admin;Trusted_Connection=True;MultipleActiveResultSets=true" + }, + "Serilog": { + "MinimumLevel": "Information", + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": "Log\\skoruba_admin.txt", + "rollingInterval": "Day" + } + }, + { + "Name": "MSSqlServer", + "Args": { + "connectionString": "Server=(localdb)\\mssqllocaldb;Database=IdentityServer4Admin;Trusted_Connection=True;MultipleActiveResultSets=true", + "tableName": "Log", + "columnOptionsSection": { + "addStandardColumns": [ "LogEvent" ], + "removeStandardColumns": [ "Properties" ] + } + } + } + ] + }, + "CertificateConfiguration": { - "UseTemporarySigningKeyForDevelopment": true, + "UseTemporarySigningKeyForDevelopment": true, - "UseSigningCertificateThumbprint": false, - "SigningCertificateThumbprint": "", + "UseSigningCertificateThumbprint": false, + "SigningCertificateThumbprint": "", - "UseSigningCertificatePfxFile": false, - "SigningCertificatePfxFilePath": "", - "SigningCertificatePfxFilePassword": "", + "UseSigningCertificatePfxFile": false, + "SigningCertificatePfxFilePath": "", + "SigningCertificatePfxFilePassword": "", - "UseValidationCertificatePfxFile": false, - "ValidationCertificatePfxFilePath": "", - "ValidationCertificatePfxFilePassword": "", + "UseValidationCertificatePfxFile": false, + "ValidationCertificatePfxFilePath": "", + "ValidationCertificatePfxFilePassword": "", - "UseValidationCertificateThumbprint": false, - "ValidationCertificateThumbprint": "" - } + "UseValidationCertificateThumbprint": false, + "ValidationCertificateThumbprint": "" + }, + "ExternalProvidersConfiguration": { + "UseGitHubProvider": false, + "GitHubClientId": "", + "GitHubClientSecret": "" + }, + "SmtpConfiguration": { + "Host": "", + "Login": "", + "Password": "" + } } \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/wwwroot/js/qrcode.js b/src/Skoruba.IdentityServer4.STS.Identity/wwwroot/js/qrcode.js new file mode 100644 index 000000000..5507c154f --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/wwwroot/js/qrcode.js @@ -0,0 +1,614 @@ +/** + * @fileoverview + * - Using the 'QRCode for Javascript library' + * - Fixed dataset of 'QRCode for Javascript library' for support full-spec. + * - this library has no dependencies. + * + * @author davidshimjs + * @see http://www.d-project.com/ + * @see http://jeromeetienne.github.com/jquery-qrcode/ + */ +var QRCode; + +(function () { + //--------------------------------------------------------------------- + // QRCode for JavaScript + // + // Copyright (c) 2009 Kazuhiko Arase + // + // URL: http://www.d-project.com/ + // + // Licensed under the MIT license: + // http://www.opensource.org/licenses/mit-license.php + // + // The word "QR Code" is registered trademark of + // DENSO WAVE INCORPORATED + // http://www.denso-wave.com/qrcode/faqpatent-e.html + // + //--------------------------------------------------------------------- + function QR8bitByte(data) { + this.mode = QRMode.MODE_8BIT_BYTE; + this.data = data; + this.parsedData = []; + + // Added to support UTF-8 Characters + for (var i = 0, l = this.data.length; i < l; i++) { + var byteArray = []; + var code = this.data.charCodeAt(i); + + if (code > 0x10000) { + byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18); + byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12); + byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6); + byteArray[3] = 0x80 | (code & 0x3F); + } else if (code > 0x800) { + byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12); + byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6); + byteArray[2] = 0x80 | (code & 0x3F); + } else if (code > 0x80) { + byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6); + byteArray[1] = 0x80 | (code & 0x3F); + } else { + byteArray[0] = code; + } + + this.parsedData.push(byteArray); + } + + this.parsedData = Array.prototype.concat.apply([], this.parsedData); + + if (this.parsedData.length != this.data.length) { + this.parsedData.unshift(191); + this.parsedData.unshift(187); + this.parsedData.unshift(239); + } + } + + QR8bitByte.prototype = { + getLength: function (buffer) { + return this.parsedData.length; + }, + write: function (buffer) { + for (var i = 0, l = this.parsedData.length; i < l; i++) { + buffer.put(this.parsedData[i], 8); + } + } + }; + + function QRCodeModel(typeNumber, errorCorrectLevel) { + this.typeNumber = typeNumber; + this.errorCorrectLevel = errorCorrectLevel; + this.modules = null; + this.moduleCount = 0; + this.dataCache = null; + this.dataList = []; + } + + QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);} + return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row=7){this.setupTypeNumber(test);} + if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);} + this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}} + return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;} + for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}} + for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}} + this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex>>bitIndex)&1)==1);} + var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;} + this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}} + row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;itotalDataCount*8){throw new Error("code length overflow. (" + +buffer.getLengthInBits() + +">" + +totalDataCount*8 + +")");} + if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);} + while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);} + while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;} + buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;} + buffer.put(QRCodeModel.PAD1,8);} + return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r=0)?modPoly.get(modIndex):0;}} + var totalCodeCount=0;for(var i=0;i=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));} + return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));} + return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;} + return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i5){lostPoint+=(3+sameCount-5);}}} + for(var row=0;row=256){n-=255;} + return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);} + if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));} + this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]]; + + function _isSupportCanvas() { + return typeof CanvasRenderingContext2D != "undefined"; + } + + // android 2.x doesn't support Data-URI spec + function _getAndroid() { + var android = false; + var sAgent = navigator.userAgent; + + if (/android/i.test(sAgent)) { // android + android = true; + var aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i); + + if (aMat && aMat[1]) { + android = parseFloat(aMat[1]); + } + } + + return android; + } + + var svgDrawer = (function() { + + var Drawing = function (el, htOption) { + this._el = el; + this._htOption = htOption; + }; + + Drawing.prototype.draw = function (oQRCode) { + var _htOption = this._htOption; + var _el = this._el; + var nCount = oQRCode.getModuleCount(); + var nWidth = Math.floor(_htOption.width / nCount); + var nHeight = Math.floor(_htOption.height / nCount); + + this.clear(); + + function makeSVG(tag, attrs) { + var el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (var k in attrs) + if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]); + return el; + } + + var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight}); + svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"); + _el.appendChild(svg); + + svg.appendChild(makeSVG("rect", {"fill": _htOption.colorLight, "width": "100%", "height": "100%"})); + svg.appendChild(makeSVG("rect", {"fill": _htOption.colorDark, "width": "1", "height": "1", "id": "template"})); + + for (var row = 0; row < nCount; row++) { + for (var col = 0; col < nCount; col++) { + if (oQRCode.isDark(row, col)) { + var child = makeSVG("use", {"x": String(col), "y": String(row)}); + child.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template") + svg.appendChild(child); + } + } + } + }; + Drawing.prototype.clear = function () { + while (this._el.hasChildNodes()) + this._el.removeChild(this._el.lastChild); + }; + return Drawing; + })(); + + var useSVG = document.documentElement.tagName.toLowerCase() === "svg"; + + // Drawing in DOM by using Table tag + var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () { + var Drawing = function (el, htOption) { + this._el = el; + this._htOption = htOption; + }; + + /** + * Draw the QRCode + * + * @param {QRCode} oQRCode + */ + Drawing.prototype.draw = function (oQRCode) { + var _htOption = this._htOption; + var _el = this._el; + var nCount = oQRCode.getModuleCount(); + var nWidth = Math.floor(_htOption.width / nCount); + var nHeight = Math.floor(_htOption.height / nCount); + var aHTML = ['']; + + for (var row = 0; row < nCount; row++) { + aHTML.push(''); + + for (var col = 0; col < nCount; col++) { + aHTML.push(''); + } + + aHTML.push(''); + } + + aHTML.push('
    '); + _el.innerHTML = aHTML.join(''); + + // Fix the margin values as real size. + var elTable = _el.childNodes[0]; + var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2; + var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2; + + if (nLeftMarginTable > 0 && nTopMarginTable > 0) { + elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px"; + } + }; + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._el.innerHTML = ''; + }; + + return Drawing; + })() : (function () { // Drawing in Canvas + function _onMakeImage() { + this._elImage.src = this._elCanvas.toDataURL("image/png"); + this._elImage.style.display = "block"; + this._elCanvas.style.display = "none"; + } + + // Android 2.1 bug workaround + // http://code.google.com/p/android/issues/detail?id=5141 + if (this._android && this._android <= 2.1) { + var factor = 1 / window.devicePixelRatio; + var drawImage = CanvasRenderingContext2D.prototype.drawImage; + CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) { + if (("nodeName" in image) && /img/i.test(image.nodeName)) { + for (var i = arguments.length - 1; i >= 1; i--) { + arguments[i] = arguments[i] * factor; + } + } else if (typeof dw == "undefined") { + arguments[1] *= factor; + arguments[2] *= factor; + arguments[3] *= factor; + arguments[4] *= factor; + } + + drawImage.apply(this, arguments); + }; + } + + /** + * Check whether the user's browser supports Data URI or not + * + * @private + * @param {Function} fSuccess Occurs if it supports Data URI + * @param {Function} fFail Occurs if it doesn't support Data URI + */ + function _safeSetDataURI(fSuccess, fFail) { + var self = this; + self._fFail = fFail; + self._fSuccess = fSuccess; + + // Check it just once + if (self._bSupportDataURI === null) { + var el = document.createElement("img"); + var fOnError = function() { + self._bSupportDataURI = false; + + if (self._fFail) { + self._fFail.call(self); + } + }; + var fOnSuccess = function() { + self._bSupportDataURI = true; + + if (self._fSuccess) { + self._fSuccess.call(self); + } + }; + + el.onabort = fOnError; + el.onerror = fOnError; + el.onload = fOnSuccess; + el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data. + return; + } else if (self._bSupportDataURI === true && self._fSuccess) { + self._fSuccess.call(self); + } else if (self._bSupportDataURI === false && self._fFail) { + self._fFail.call(self); + } + }; + + /** + * Drawing QRCode by using canvas + * + * @constructor + * @param {HTMLElement} el + * @param {Object} htOption QRCode Options + */ + var Drawing = function (el, htOption) { + this._bIsPainted = false; + this._android = _getAndroid(); + + this._htOption = htOption; + this._elCanvas = document.createElement("canvas"); + this._elCanvas.width = htOption.width; + this._elCanvas.height = htOption.height; + el.appendChild(this._elCanvas); + this._el = el; + this._oContext = this._elCanvas.getContext("2d"); + this._bIsPainted = false; + this._elImage = document.createElement("img"); + this._elImage.alt = "Scan me!"; + this._elImage.style.display = "none"; + this._el.appendChild(this._elImage); + this._bSupportDataURI = null; + }; + + /** + * Draw the QRCode + * + * @param {QRCode} oQRCode + */ + Drawing.prototype.draw = function (oQRCode) { + var _elImage = this._elImage; + var _oContext = this._oContext; + var _htOption = this._htOption; + + var nCount = oQRCode.getModuleCount(); + var nWidth = _htOption.width / nCount; + var nHeight = _htOption.height / nCount; + var nRoundedWidth = Math.round(nWidth); + var nRoundedHeight = Math.round(nHeight); + + _elImage.style.display = "none"; + this.clear(); + + for (var row = 0; row < nCount; row++) { + for (var col = 0; col < nCount; col++) { + var bIsDark = oQRCode.isDark(row, col); + var nLeft = col * nWidth; + var nTop = row * nHeight; + _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; + _oContext.lineWidth = 1; + _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; + _oContext.fillRect(nLeft, nTop, nWidth, nHeight); + + // 안티 앨리어싱 방지 처리 + _oContext.strokeRect( + Math.floor(nLeft) + 0.5, + Math.floor(nTop) + 0.5, + nRoundedWidth, + nRoundedHeight + ); + + _oContext.strokeRect( + Math.ceil(nLeft) - 0.5, + Math.ceil(nTop) - 0.5, + nRoundedWidth, + nRoundedHeight + ); + } + } + + this._bIsPainted = true; + }; + + /** + * Make the image from Canvas if the browser supports Data URI. + */ + Drawing.prototype.makeImage = function () { + if (this._bIsPainted) { + _safeSetDataURI.call(this, _onMakeImage); + } + }; + + /** + * Return whether the QRCode is painted or not + * + * @return {Boolean} + */ + Drawing.prototype.isPainted = function () { + return this._bIsPainted; + }; + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height); + this._bIsPainted = false; + }; + + /** + * @private + * @param {Number} nNumber + */ + Drawing.prototype.round = function (nNumber) { + if (!nNumber) { + return nNumber; + } + + return Math.floor(nNumber * 1000) / 1000; + }; + + return Drawing; + })(); + + /** + * Get the type by string length + * + * @private + * @param {String} sText + * @param {Number} nCorrectLevel + * @return {Number} type + */ + function _getTypeNumber(sText, nCorrectLevel) { + var nType = 1; + var length = _getUTF8Length(sText); + + for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) { + var nLimit = 0; + + switch (nCorrectLevel) { + case QRErrorCorrectLevel.L : + nLimit = QRCodeLimitLength[i][0]; + break; + case QRErrorCorrectLevel.M : + nLimit = QRCodeLimitLength[i][1]; + break; + case QRErrorCorrectLevel.Q : + nLimit = QRCodeLimitLength[i][2]; + break; + case QRErrorCorrectLevel.H : + nLimit = QRCodeLimitLength[i][3]; + break; + } + + if (length <= nLimit) { + break; + } else { + nType++; + } + } + + if (nType > QRCodeLimitLength.length) { + throw new Error("Too long data"); + } + + return nType; + } + + function _getUTF8Length(sText) { + var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a'); + return replacedText.length + (replacedText.length != sText ? 3 : 0); + } + + /** + * @class QRCode + * @constructor + * @example + * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie"); + * + * @example + * var oQRCode = new QRCode("test", { + * text : "http://naver.com", + * width : 128, + * height : 128 + * }); + * + * oQRCode.clear(); // Clear the QRCode. + * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode. + * + * @param {HTMLElement|String} el target element or 'id' attribute of element. + * @param {Object|String} vOption + * @param {String} vOption.text QRCode link data + * @param {Number} [vOption.width=256] + * @param {Number} [vOption.height=256] + * @param {String} [vOption.colorDark="#000000"] + * @param {String} [vOption.colorLight="#ffffff"] + * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H] + */ + QRCode = function (el, vOption) { + this._htOption = { + width : 256, + height : 256, + typeNumber : 4, + colorDark : "#000000", + colorLight : "#ffffff", + correctLevel : QRErrorCorrectLevel.H + }; + + if (typeof vOption === 'string') { + vOption = { + text : vOption + }; + } + + // Overwrites options + if (vOption) { + for (var i in vOption) { + this._htOption[i] = vOption[i]; + } + } + + if (typeof el == "string") { + el = document.getElementById(el); + } + + if (this._htOption.useSVG) { + Drawing = svgDrawer; + } + + this._android = _getAndroid(); + this._el = el; + this._oQRCode = null; + this._oDrawing = new Drawing(this._el, this._htOption); + + if (this._htOption.text) { + this.makeCode(this._htOption.text); + } + }; + + /** + * Make the QRCode + * + * @param {String} sText link data + */ + QRCode.prototype.makeCode = function (sText) { + this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel); + this._oQRCode.addData(sText); + this._oQRCode.make(); + this._el.title = sText; + this._oDrawing.draw(this._oQRCode); + this.makeImage(); + }; + + /** + * Make the Image from Canvas element + * - It occurs automatically + * - Android below 3 doesn't support Data-URI spec. + * + * @private + */ + QRCode.prototype.makeImage = function () { + if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) { + this._oDrawing.makeImage(); + } + }; + + /** + * Clear the QRCode + */ + QRCode.prototype.clear = function () { + this._oDrawing.clear(); + }; + + /** + * @name QRCode.CorrectLevel + */ + QRCode.CorrectLevel = QRErrorCorrectLevel; +})(); diff --git a/src/Skoruba.IdentityServer4.STS.Identity/wwwroot/js/qrcode.min.js b/src/Skoruba.IdentityServer4.STS.Identity/wwwroot/js/qrcode.min.js new file mode 100644 index 000000000..993e88f39 --- /dev/null +++ b/src/Skoruba.IdentityServer4.STS.Identity/wwwroot/js/qrcode.min.js @@ -0,0 +1 @@ +var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j=0?p.get(q):0}}for(var r=0,m=0;mm;m++)for(var j=0;jm;m++)for(var j=0;j=0;)b^=f.G15<=0;)b^=f.G18<>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;cf;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=[''],h=0;d>h;h++){g.push("");for(var i=0;d>i;i++)g.push('');g.push("")}g.push("
    "),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}(); \ No newline at end of file diff --git a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Controllers/ConfigurationControllerTests.cs b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Controllers/ConfigurationControllerTests.cs index f629f9e35..6d4439874 100644 --- a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Controllers/ConfigurationControllerTests.cs +++ b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Controllers/ConfigurationControllerTests.cs @@ -803,8 +803,8 @@ private IServiceProvider GetServices() services.AddAdminServices(); - services.AddAdminAspNetIdentityServices, int, RoleDto, int, int, int, - UserIdentity, UserIdentityRole, int, UserIdentityUserClaim, UserIdentityUserRole, + services.AddAdminAspNetIdentityServices, string, RoleDto, string, string, string, + UserIdentity, UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken>(); services.AddSession(); diff --git a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Controllers/IdentityControllerTests.cs b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Controllers/IdentityControllerTests.cs index 3133cad0a..02cbddc77 100644 --- a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Controllers/IdentityControllerTests.cs +++ b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Controllers/IdentityControllerTests.cs @@ -31,12 +31,12 @@ namespace Skoruba.IdentityServer4.Admin.UnitTests.Controllers { public class IdentityControllerTests { - private IIdentityService, int, RoleDto, int, int, int, - UserIdentity, UserIdentityRole, int, UserIdentityUserClaim, UserIdentityUserRole, + private IIdentityService, string, RoleDto, string, string, string, + UserIdentity, UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken> GetIdentityService(IServiceProvider services) { - return services.GetRequiredService, int, RoleDto, int, int, int, - UserIdentity, UserIdentityRole, int, UserIdentityUserClaim, UserIdentityUserRole, + return services.GetRequiredService, string, RoleDto, string, string, string, + UserIdentity, UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken>>(); } @@ -51,7 +51,7 @@ public async Task AddUser() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); var result = await controller.UserProfile(userDto); // Assert @@ -61,7 +61,7 @@ public async Task AddUser() var user = await dbContext.Users.Where(x => x.UserName == userDto.UserName).SingleOrDefaultAsync(); userDto.Id = user.Id; - var addedUser = await identityService.GetUserAsync(userDto.Id.ToString()); + var addedUser = await identityService.GetUserAsync(userDto.Id); userDto.ShouldBeEquivalentTo(addedUser, opts => opts.Excluding(x => x.Id)); } @@ -76,7 +76,7 @@ public async Task DeleteUser() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); @@ -103,13 +103,13 @@ public async Task UpdateUser() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); //Get inserted userid var userId = await dbContext.Users.Where(x => x.UserName == userDto.UserName).Select(x => x.Id).SingleOrDefaultAsync(); - var updatedUserDto = IdentityDtoMock.GenerateRandomUser(userId); + var updatedUserDto = IdentityDtoMock.GenerateRandomUser(userId); var result = await controller.UserProfile(updatedUserDto); @@ -132,7 +132,7 @@ public async Task GetUser() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); //Add user await identityService.CreateUserAsync(userDto); @@ -148,7 +148,7 @@ public async Task GetUser() viewResult.ViewName.Should().Be("UserProfile"); viewResult.ViewData.Should().NotBeNull(); - var viewModel = Assert.IsType>(viewResult.ViewData.Model); + var viewModel = Assert.IsType>(viewResult.ViewData.Model); userDto.Id = userId; var addedUser = await identityService.GetUserAsync(userDto.Id.ToString()); @@ -165,7 +165,7 @@ public async Task AddRole() // Get controller var controller = PrepareIdentityController(serviceProvider); - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); var result = await controller.Role(roleDto); // Assert @@ -190,7 +190,7 @@ public async Task GetRole() // Get controller var controller = PrepareIdentityController(serviceProvider); - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); var roleId = await dbContext.Roles.Where(x => x.Name == roleDto.Name).Select(x => x.Id).SingleOrDefaultAsync(); @@ -201,7 +201,7 @@ public async Task GetRole() viewResult.ViewName.Should().BeNull(); viewResult.ViewData.Should().NotBeNull(); - var viewModel = Assert.IsType>(viewResult.ViewData.Model); + var viewModel = Assert.IsType>(viewResult.ViewData.Model); roleDto.Id = roleId; var addedRole = await identityService.GetRoleAsync(roleDto.Id.ToString()); @@ -218,7 +218,7 @@ public async Task DeleteRole() // Get controller var controller = PrepareIdentityController(serviceProvider); - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); @@ -245,12 +245,12 @@ public async Task UpdateRole() // Get controller var controller = PrepareIdentityController(serviceProvider); - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); var roleId = await dbContext.Roles.Where(x => x.Name == roleDto.Name).Select(x => x.Id).SingleOrDefaultAsync(); - var updatedRoleDto = IdentityDtoMock.GenerateRandomRole(roleId); + var updatedRoleDto = IdentityDtoMock.GenerateRandomRole(roleId); var result = await controller.Role(updatedRoleDto); @@ -273,13 +273,13 @@ public async Task AddUserClaim() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); var user = await dbContext.Users.Where(x => x.UserName == userDto.UserName).SingleOrDefaultAsync(); userDto.Id = user.Id; - var userClaimsDto = IdentityDtoMock.GenerateRandomUserClaim(0, user.Id); + var userClaimsDto = IdentityDtoMock.GenerateRandomUserClaim(0, user.Id); var result = await controller.UserClaims(userClaimsDto); // Assert @@ -303,10 +303,10 @@ public async Task AddUserRole() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); var user = await dbContext.Users.Where(x => x.UserName == userDto.UserName).SingleOrDefaultAsync(); @@ -315,7 +315,7 @@ public async Task AddUserRole() var role = await dbContext.Roles.Where(x => x.Name == roleDto.Name).SingleOrDefaultAsync(); roleDto.Id = role.Id; - var userRoleDto = IdentityDtoMock.GenerateRandomUserRole>(roleDto.Id, user.Id); + var userRoleDto = IdentityDtoMock.GenerateRandomUserRole>(roleDto.Id, user.Id); var result = await controller.UserRoles(userRoleDto); // Assert @@ -341,10 +341,10 @@ public async Task DeleteUserRole() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); var user = await dbContext.Users.Where(x => x.UserName == userDto.UserName).SingleOrDefaultAsync(); @@ -353,7 +353,7 @@ public async Task DeleteUserRole() var role = await dbContext.Roles.Where(x => x.Name == roleDto.Name).SingleOrDefaultAsync(); roleDto.Id = role.Id; - var userRoleDto = IdentityDtoMock.GenerateRandomUserRole>(roleDto.Id, user.Id); + var userRoleDto = IdentityDtoMock.GenerateRandomUserRole>(roleDto.Id, user.Id); await identityService.CreateUserRoleAsync(userRoleDto); @@ -378,13 +378,13 @@ public async Task DeleteUserClaim() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); var user = await dbContext.Users.Where(x => x.UserName == userDto.UserName).SingleOrDefaultAsync(); userDto.Id = user.Id; - var userClaimsDto = IdentityDtoMock.GenerateRandomUserClaim(0, user.Id); + var userClaimsDto = IdentityDtoMock.GenerateRandomUserClaim(0, user.Id); await identityService.CreateUserClaimsAsync(userClaimsDto); var newUserClaim = await dbContext.UserClaims.Where(x => x.ClaimValue == userClaimsDto.ClaimValue).SingleOrDefaultAsync(); userClaimsDto.ClaimId = newUserClaim.Id; @@ -410,13 +410,13 @@ public async Task AddRoleClaim() // Get controller var controller = PrepareIdentityController(serviceProvider); - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); var role = await dbContext.Roles.Where(x => x.Name == roleDto.Name).SingleOrDefaultAsync(); roleDto.Id = role.Id; - var roleClaimsDto = IdentityDtoMock.GenerateRandomRoleClaim(0, role.Id); + var roleClaimsDto = IdentityDtoMock.GenerateRandomRoleClaim(0, role.Id); var result = await controller.RoleClaims(roleClaimsDto); // Assert @@ -441,13 +441,13 @@ public async Task DeleteRoleClaim() // Get controller var controller = PrepareIdentityController(serviceProvider); - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); var role = await dbContext.Roles.Where(x => x.Name == roleDto.Name).SingleOrDefaultAsync(); roleDto.Id = role.Id; - var roleClaimsDto = IdentityDtoMock.GenerateRandomRoleClaim(0, role.Id); + var roleClaimsDto = IdentityDtoMock.GenerateRandomRoleClaim(0, role.Id); await identityService.CreateRoleClaimsAsync(roleClaimsDto); var newRoleClaim = await dbContext.RoleClaims.Where(x => x.ClaimValue == roleClaimsDto.ClaimValue).SingleOrDefaultAsync(); roleClaimsDto.ClaimId = newRoleClaim.Id; @@ -473,13 +473,13 @@ public async Task UserChangePassword() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); var userId = await dbContext.Users.Where(x => x.UserName == userDto.UserName).Select(x => x.Id).SingleOrDefaultAsync(); - var changePassword = IdentityDtoMock.GenerateRandomUserChangePassword(userId, "IdentityServer4!"); + var changePassword = IdentityDtoMock.GenerateRandomUserChangePassword(userId, "IdentityServer4!"); var result = await controller.UserChangePassword(changePassword); @@ -501,7 +501,7 @@ public async Task UserProvidersDelete() // Get controller var controller = PrepareIdentityController(serviceProvider); - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); var userId = await dbContext.Users.Where(x => x.UserName == userDto.UserName).Select(x => x.Id).SingleOrDefaultAsync(); @@ -513,7 +513,7 @@ public async Task UserProvidersDelete() await dbContext.UserLogins.AddAsync(provider); await dbContext.SaveChangesAsync(); - var providersDto = IdentityDtoMock.GenerateRandomUserProviders(randomProviderKey, randomProviderLogin, userId); + var providersDto = IdentityDtoMock.GenerateRandomUserProviders(randomProviderKey, randomProviderLogin, userId); var result = await controller.UserProvidersDelete(providersDto); // Assert @@ -567,8 +567,8 @@ private IServiceProvider GetServices() services.AddAdminServices(); - services.AddAdminAspNetIdentityServices, int, RoleDto, int, int, int, - UserIdentity, UserIdentityRole, int, UserIdentityUserClaim, UserIdentityUserRole, + services.AddAdminAspNetIdentityServices, string, RoleDto, string, string, string, + UserIdentity, UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken>(); services.AddIdentity() diff --git a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Mocks/IdentityDtoMock.cs b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Mocks/IdentityDtoMock.cs index db72e747b..d2704d98c 100644 --- a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Mocks/IdentityDtoMock.cs +++ b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Mocks/IdentityDtoMock.cs @@ -49,14 +49,14 @@ public static UserChangePasswordDto GenerateRandomUserChangePassword(TKey return userChangePassword; } - public static UserDto GenerateRandomUser(TKey id) + public static UserDto GenerateRandomUser(TKey id = default(TKey)) { var user = GetUserFaker(id).Generate(); return user; } - public static RoleDto GenerateRandomRole(TKey id) + public static RoleDto GenerateRandomRole(TKey id = default(TKey)) { var role = GetRoleFaker(id).Generate(); @@ -74,7 +74,7 @@ public static Faker> GetUserClaimsFaker(int id, TKey userId) return userClaimFaker; } - public static UserClaimsDto GenerateRandomUserClaim(int id, TKey userId) + public static UserClaimsDto GenerateRandomUserClaim(int id, TKey userId = default(TKey)) { var userClaim = GetUserClaimsFaker(id, userId).Generate(); diff --git a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Mocks/IdentityMock.cs b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Mocks/IdentityMock.cs index 723bf8ea8..cec239739 100644 --- a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Mocks/IdentityMock.cs +++ b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Mocks/IdentityMock.cs @@ -12,7 +12,7 @@ namespace Skoruba.IdentityServer4.Admin.UnitTests.Mocks { public static class IdentityMock { - public static Faker GetUserProvidersFaker(string key, string loginProvider, int userId) + public static Faker GetUserProvidersFaker(string key, string loginProvider, string userId) { var userProvidersFaker = new Faker() .RuleFor(o => o.LoginProvider, f => loginProvider) @@ -23,7 +23,7 @@ public static Faker GetUserProvidersFaker(string key, str return userProvidersFaker; } - public static UserIdentityUserLogin GenerateRandomUserProviders(string key, string loginProvider, int userId) + public static UserIdentityUserLogin GenerateRandomUserProviders(string key, string loginProvider, string userId) { var provider = GetUserProvidersFaker(key, loginProvider, userId).Generate(); diff --git a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Repositories/PersistedGrantRepositoryTests.cs b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Repositories/PersistedGrantRepositoryTests.cs index 698b2e9d3..fbafe2816 100644 --- a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Repositories/PersistedGrantRepositoryTests.cs +++ b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Repositories/PersistedGrantRepositoryTests.cs @@ -30,9 +30,9 @@ public PersistedGrantRepositoryTests() private readonly ConfigurationStoreOptions _storeOptions; private readonly OperationalStoreOptions _operationalStore; - private IPersistedGrantAspNetIdentityRepository GetPersistedGrantRepository(AdminDbContext context) + private IPersistedGrantAspNetIdentityRepository GetPersistedGrantRepository(AdminDbContext context) { - var persistedGrantRepository = new PersistedGrantAspNetIdentityRepository(context); + var persistedGrantRepository = new PersistedGrantAspNetIdentityRepository(context); return persistedGrantRepository; } diff --git a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Services/IdentityServiceTests.cs b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Services/IdentityServiceTests.cs index fd850573b..ee9d00168 100644 --- a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Services/IdentityServiceTests.cs +++ b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Services/IdentityServiceTests.cs @@ -39,34 +39,34 @@ public IdentityServiceTests() private readonly ConfigurationStoreOptions _storeOptions; private readonly OperationalStoreOptions _operationalStore; - private IIdentityRepository GetIdentityRepository(AdminDbContext dbContext, UserManager userManager, RoleManager roleManager, IMapper mapper) { - return new IdentityRepository(dbContext, userManager, roleManager, mapper); } - private IIdentityService, int, RoleDto, int, int, int, UserIdentity, - UserIdentityRole, int, + private IIdentityService, string, RoleDto, string, string, string, UserIdentity, + UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, - UserIdentityUserToken> GetIdentityService(IIdentityRepository identityRepository, + UserIdentityUserToken> GetIdentityService(IIdentityRepository identityRepository, IIdentityServiceResources identityServiceResources, IMapper mapper) { - return new IdentityService, int, RoleDto, int, int, int, UserIdentity, - UserIdentityRole, int, + return new IdentityService, string, RoleDto, string, string, string, UserIdentity, + UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken>(identityRepository, identityServiceResources, mapper); } private IMapper GetMapper() { - return new MapperConfiguration(cfg => cfg.AddProfile, int, RoleDto, int, UserIdentity, UserIdentityRole, int, + return new MapperConfiguration(cfg => cfg.AddProfile, string, RoleDto, string, UserIdentity, UserIdentityRole, string, UserIdentityUserClaim, UserIdentityUserRole, UserIdentityUserLogin, UserIdentityRoleClaim, UserIdentityUserToken> >()) @@ -75,14 +75,14 @@ private IMapper GetMapper() private UserManager GetTestUserManager(AdminDbContext context) { - var testUserManager = IdentityMock.TestUserManager(new UserStore(context, new IdentityErrorDescriber())); + var testUserManager = IdentityMock.TestUserManager(new UserStore(context, new IdentityErrorDescriber())); return testUserManager; } private RoleManager GetTestRoleManager(AdminDbContext context) { - var testRoleManager = IdentityMock.TestRoleManager(new RoleStore(context, new IdentityErrorDescriber())); + var testRoleManager = IdentityMock.TestRoleManager(new RoleStore(context, new IdentityErrorDescriber())); return testRoleManager; } @@ -101,7 +101,7 @@ public async Task AddUserAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new user - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); @@ -131,7 +131,7 @@ public async Task DeleteUserProviderAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new user - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); @@ -155,7 +155,7 @@ public async Task DeleteUserProviderAsync() var addedUserProvider = await context.UserLogins.Where(x => x.ProviderKey == userProvider.ProviderKey && x.LoginProvider == userProvider.LoginProvider).SingleOrDefaultAsync(); addedUserProvider.Should().NotBeNull(); - var userProviderDto = IdentityDtoMock.GenerateRandomUserProviders(userProvider.ProviderKey, userProvider.LoginProvider, + var userProviderDto = IdentityDtoMock.GenerateRandomUserProviders(userProvider.ProviderKey, userProvider.LoginProvider, userProvider.UserId); await identityService.DeleteUserProvidersAsync(userProviderDto); @@ -181,7 +181,7 @@ public async Task AddUserRoleAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new user - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); @@ -195,7 +195,7 @@ public async Task AddUserRoleAsync() userDto.ShouldBeEquivalentTo(newUserDto); //Generate random new role - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); @@ -208,7 +208,7 @@ public async Task AddUserRoleAsync() //Assert new role roleDto.ShouldBeEquivalentTo(newRoleDto); - var userRoleDto = IdentityDtoMock.GenerateRandomUserRole>(roleDto.Id, userDto.Id); + var userRoleDto = IdentityDtoMock.GenerateRandomUserRole>(roleDto.Id, userDto.Id); await identityService.CreateUserRoleAsync(userRoleDto); @@ -234,7 +234,7 @@ public async Task DeleteUserRoleAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new user - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); @@ -248,7 +248,7 @@ public async Task DeleteUserRoleAsync() userDto.ShouldBeEquivalentTo(newUserDto); //Generate random new role - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); @@ -261,7 +261,7 @@ public async Task DeleteUserRoleAsync() //Assert new role roleDto.ShouldBeEquivalentTo(newRoleDto); - var userRoleDto = IdentityDtoMock.GenerateRandomUserRole>(roleDto.Id, userDto.Id); + var userRoleDto = IdentityDtoMock.GenerateRandomUserRole>(roleDto.Id, userDto.Id); await identityService.CreateUserRoleAsync(userRoleDto); @@ -292,7 +292,7 @@ public async Task AddUserClaimAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new user - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); @@ -306,7 +306,7 @@ public async Task AddUserClaimAsync() userDto.ShouldBeEquivalentTo(newUserDto); //Generate random new user claim - var userClaimDto = IdentityDtoMock.GenerateRandomUserClaim(0, userDto.Id); + var userClaimDto = IdentityDtoMock.GenerateRandomUserClaim(0, userDto.Id); await identityService.CreateUserClaimsAsync(userClaimDto); @@ -336,7 +336,7 @@ public async Task DeleteUserClaimAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new user - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); @@ -350,7 +350,7 @@ public async Task DeleteUserClaimAsync() userDto.ShouldBeEquivalentTo(newUserDto); //Generate random new user claim - var userClaimDto = IdentityDtoMock.GenerateRandomUserClaim(0, userDto.Id); + var userClaimDto = IdentityDtoMock.GenerateRandomUserClaim(0, userDto.Id); await identityService.CreateUserClaimsAsync(userClaimDto); @@ -386,7 +386,7 @@ public async Task UpdateUserAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new user - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); @@ -403,7 +403,7 @@ public async Task UpdateUserAsync() context.Entry(user).State = EntityState.Detached; //Generete new user with added item id - var userDtoForUpdate = IdentityDtoMock.GenerateRandomUser(user.Id); + var userDtoForUpdate = IdentityDtoMock.GenerateRandomUser(user.Id); //Update user await identityService.UpdateUserAsync(userDtoForUpdate); @@ -430,7 +430,7 @@ public async Task DeleteUserAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new user - var userDto = IdentityDtoMock.GenerateRandomUser(0); + var userDto = IdentityDtoMock.GenerateRandomUser(); await identityService.CreateUserAsync(userDto); @@ -470,7 +470,7 @@ public async Task AddRoleAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new role - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); @@ -500,7 +500,7 @@ public async Task UpdateRoleAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new role - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); @@ -517,7 +517,7 @@ public async Task UpdateRoleAsync() context.Entry(role).State = EntityState.Detached; //Generete new role with added item id - var roleDtoForUpdate = IdentityDtoMock.GenerateRandomRole(role.Id); + var roleDtoForUpdate = IdentityDtoMock.GenerateRandomRole(role.Id); //Update role await identityService.UpdateRoleAsync(roleDtoForUpdate); @@ -544,7 +544,7 @@ public async Task DeleteRoleAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new role - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); @@ -584,7 +584,7 @@ public async Task AddRoleClaimAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new role - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); @@ -598,7 +598,7 @@ public async Task AddRoleClaimAsync() roleDto.ShouldBeEquivalentTo(newRoleDto); //Generate random new role claim - var roleClaimDto = IdentityDtoMock.GenerateRandomRoleClaim(0, roleDto.Id); + var roleClaimDto = IdentityDtoMock.GenerateRandomRoleClaim(0, roleDto.Id); await identityService.CreateRoleClaimsAsync(roleClaimDto); @@ -628,7 +628,7 @@ public async Task RemoveRoleClaimAsync() var identityService = GetIdentityService(identityRepository, localizerIdentityResource, mapper); //Generate random new role - var roleDto = IdentityDtoMock.GenerateRandomRole(0); + var roleDto = IdentityDtoMock.GenerateRandomRole(); await identityService.CreateRoleAsync(roleDto); @@ -642,7 +642,7 @@ public async Task RemoveRoleClaimAsync() roleDto.ShouldBeEquivalentTo(newRoleDto); //Generate random new role claim - var roleClaimDto = IdentityDtoMock.GenerateRandomRoleClaim(0, roleDto.Id); + var roleClaimDto = IdentityDtoMock.GenerateRandomRoleClaim(0, roleDto.Id); await identityService.CreateRoleClaimsAsync(roleClaimDto); diff --git a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Services/PersistedGrantServiceTests.cs b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Services/PersistedGrantServiceTests.cs index e809340fe..8e120f67a 100644 --- a/tests/Skoruba.IdentityServer4.Admin.UnitTests/Services/PersistedGrantServiceTests.cs +++ b/tests/Skoruba.IdentityServer4.Admin.UnitTests/Services/PersistedGrantServiceTests.cs @@ -34,18 +34,18 @@ public PersistedGrantServiceTests() private readonly ConfigurationStoreOptions _storeOptions; private readonly OperationalStoreOptions _operationalStore; - private IPersistedGrantAspNetIdentityRepository GetPersistedGrantRepository(AdminDbContext context) + private IPersistedGrantAspNetIdentityRepository GetPersistedGrantRepository(AdminDbContext context) { - var persistedGrantRepository = new PersistedGrantAspNetIdentityRepository(context); + var persistedGrantRepository = new PersistedGrantAspNetIdentityRepository(context); return persistedGrantRepository; } - private IPersistedGrantAspNetIdentityService - GetPersistedGrantService(IPersistedGrantAspNetIdentityRepository repository, IPersistedGrantAspNetIdentityServiceResources persistedGrantServiceResources) + GetPersistedGrantService(IPersistedGrantAspNetIdentityRepository repository, IPersistedGrantAspNetIdentityServiceResources persistedGrantServiceResources) { - var persistedGrantService = new PersistedGrantAspNetIdentityService(repository, persistedGrantServiceResources);