diff --git a/BTCPayServer.Tests/ThirdPartyTests.cs b/BTCPayServer.Tests/ThirdPartyTests.cs index f41f72a75..bcc2b5b78 100644 --- a/BTCPayServer.Tests/ThirdPartyTests.cs +++ b/BTCPayServer.Tests/ThirdPartyTests.cs @@ -385,7 +385,8 @@ retry: EqualJsContent(expected, actual); actual = GetFileContent("BTCPayServer", "wwwroot", "vendor", "tom-select", "tom-select.complete.min.js").Trim(); - expected = (await (await client.GetAsync($"https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js")).Content.ReadAsStringAsync()).Trim(); + version = Regex.Match(actual, "Tom Select v([0-9]+.[0-9]+.[0-9]+)").Groups[1].Value; + expected = (await (await client.GetAsync($"https://cdn.jsdelivr.net/npm/tom-select@{version}/dist/js/tom-select.complete.min.js")).Content.ReadAsStringAsync()).Trim(); EqualJsContent(expected, actual); actual = GetFileContent("BTCPayServer", "wwwroot", "vendor", "dom-confetti", "dom-confetti.min.js").Trim(); diff --git a/BTCPayServer/Components/LabelManager/Default.cshtml b/BTCPayServer/Components/LabelManager/Default.cshtml index 79970cd90..cced31ead 100644 --- a/BTCPayServer/Components/LabelManager/Default.cshtml +++ b/BTCPayServer/Components/LabelManager/Default.cshtml @@ -3,7 +3,7 @@ @model BTCPayServer.Components.LabelManager.LabelViewModel @{ var elementId = "a" + Encoders.Base58.EncodeData(RandomUtils.GetBytes(16)); - var fetchUrl = Url.Action("GetLabels", "UIWallets", new { + var fetchUrl = Url.Action("LabelsJson", "UIWallets", new { walletId = Model.WalletObjectId.WalletId, excludeTypes = Safe.Json(Model.ExcludeTypes) }); diff --git a/BTCPayServer/Controllers/UIWalletsController.cs b/BTCPayServer/Controllers/UIWalletsController.cs index 5b2eb6f0f..c5704e940 100644 --- a/BTCPayServer/Controllers/UIWalletsController.cs +++ b/BTCPayServer/Controllers/UIWalletsController.cs @@ -1381,9 +1381,9 @@ namespace BTCPayServer.Controllers return Ok(); } - [HttpGet("{walletId}/labels")] + [HttpGet("{walletId}/labels.json")] [IgnoreAntiforgeryToken] - public async Task GetLabels( + public async Task LabelsJson( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, bool excludeTypes, string? type = null, @@ -1397,14 +1397,62 @@ namespace BTCPayServer.Controllers : await WalletRepository.GetWalletLabels(walletObjectId); return Ok(labels .Where(l => !excludeTypes || !WalletObjectData.Types.AllTypes.Contains(l.Label)) - .Select(tuple => new + .Select(tuple => new WalletLabelModel { - label = tuple.Label, - color = tuple.Color, - textColor = ColorPalette.Default.TextColor(tuple.Color) + Label = tuple.Label, + Color = tuple.Color, + TextColor = ColorPalette.Default.TextColor(tuple.Color) })); } + [HttpGet("{walletId}/labels")] + public async Task WalletLabels( + [ModelBinder(typeof(WalletIdModelBinder))] + WalletId walletId) + { + if (walletId.StoreId == null) + return NotFound(); + + var labels = await WalletRepository.GetWalletLabels(walletId); + + var vm = new WalletLabelsModel + { + WalletId = walletId, + Labels = labels + .Where(l => !WalletObjectData.Types.AllTypes.Contains(l.Label)) + .Select(tuple => new WalletLabelModel + { + Label = tuple.Label, + Color = tuple.Color, + TextColor = ColorPalette.Default.TextColor(tuple.Color) + }) + }; + + return View(vm); + } + + [HttpPost("{walletId}/labels/{id}/remove")] + public async Task RemoveWalletLabel( + [ModelBinder(typeof(WalletIdModelBinder))] + WalletId walletId, string id) + { + if (walletId.StoreId == null) + return NotFound(); + + var labels = new[] { id }; + ; + if (await WalletRepository.RemoveWalletLabels(walletId, labels)) + { + TempData[WellKnownTempData.SuccessMessage] = "The label has been successfully removed."; + } + else + { + TempData[WellKnownTempData.ErrorMessage] = "The label could not be removed."; + } + + return RedirectToAction(nameof(WalletLabels), new { walletId }); + } + private string GetImage(PaymentMethodId paymentMethodId, BTCPayNetwork network) { var res = paymentMethodId.PaymentType == PaymentTypes.BTCLike diff --git a/BTCPayServer/Models/WalletViewModels/WalletLabelsModel.cs b/BTCPayServer/Models/WalletViewModels/WalletLabelsModel.cs new file mode 100644 index 000000000..c7d548f68 --- /dev/null +++ b/BTCPayServer/Models/WalletViewModels/WalletLabelsModel.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace BTCPayServer.Models.WalletViewModels; + +public class WalletLabelsModel +{ + public WalletId WalletId { get; set; } + public IEnumerable Labels { get; set; } +} + +public class WalletLabelModel +{ + public string Label { get; set; } + public string Color { get; set; } + public string TextColor { get; set; } +} diff --git a/BTCPayServer/Services/WalletRepository.cs b/BTCPayServer/Services/WalletRepository.cs index 16d3d2f1e..0d3f84e05 100644 --- a/BTCPayServer/Services/WalletRepository.cs +++ b/BTCPayServer/Services/WalletRepository.cs @@ -563,6 +563,18 @@ namespace BTCPayServer.Services await RemoveWalletObjectLink(labelObjId, id); } } + + public async Task RemoveWalletLabels(WalletId id, params string[] labels) + { + ArgumentNullException.ThrowIfNull(id); + var count = 0; + foreach (var l in labels.Select(l => l.Trim())) + { + var labelObjId = new WalletObjectId(id, WalletObjectData.Types.Label, l); + count += await RemoveWalletObjects(labelObjId) ? 1 : 0; + } + return count > 0; + } public async Task SetWalletObject(WalletObjectId id, JObject? data) { diff --git a/BTCPayServer/Views/UIStores/WalletSettings.cshtml b/BTCPayServer/Views/UIStores/WalletSettings.cshtml index 19e9abe10..f7ca0e138 100644 --- a/BTCPayServer/Views/UIStores/WalletSettings.cshtml +++ b/BTCPayServer/Views/UIStores/WalletSettings.cshtml @@ -16,7 +16,6 @@ - } @@ -200,6 +199,11 @@ + +

Labels

+

+ Manage labels +

diff --git a/BTCPayServer/Views/UIWallets/WalletLabels.cshtml b/BTCPayServer/Views/UIWallets/WalletLabels.cshtml new file mode 100644 index 000000000..b79d1f560 --- /dev/null +++ b/BTCPayServer/Views/UIWallets/WalletLabels.cshtml @@ -0,0 +1,52 @@ +@using BTCPayServer.Abstractions.Models +@model WalletLabelsModel +@{ + var walletId = Context.GetRouteValue("walletId").ToString(); + Layout = "../Shared/_NavLayout.cshtml"; + ViewData.SetActivePage(WalletsNavPages.Settings, "Wallet Labels", walletId); +} + +@section PageFootContent { + +} + +

@ViewData["Title"]

+ +@if (Model.Labels.Any()) +{ + + + + + + + + + @foreach (var label in Model.Labels) + { + + + + + } + +
LabelActions
+
+ @label.Label +
+
+
+ +
+
+ +} +else +{ +

+ There are no custom labels yet. You can create custom labels by assigning them to your transactions. +

+} + diff --git a/BTCPayServer/wwwroot/main/site.js b/BTCPayServer/wwwroot/main/site.js index f55a3ea4b..c8099ff5c 100644 --- a/BTCPayServer/wwwroot/main/site.js +++ b/BTCPayServer/wwwroot/main/site.js @@ -33,7 +33,7 @@ async function initLabelManager (elementId) { : '--label-bg:var(--btcpay-neutral-300);--label-fg:var(--btcpay-neutral-800)' if (element) { - const { fetchUrl, updateUrl, walletId, walletObjectType, walletObjectId, labels,selectElement } = element.dataset; + const { fetchUrl, updateUrl, walletId, walletObjectType, walletObjectId, labels, selectElement } = element.dataset; const commonCallId = `walletLabels-${walletId}`; if (!window[commonCallId]) { window[commonCallId] = fetch(fetchUrl, { @@ -44,7 +44,6 @@ async function initLabelManager (elementId) { }, }).then(res => res.json()); } - const selectElementI = document.getElementById(selectElement); const items = element.value.split(',').filter(x => !!x); const options = await window[commonCallId].then(labels => { const newItems = items.filter(item => !labels.find(label => label.label === item)); @@ -92,7 +91,8 @@ async function initLabelManager (elementId) { })); }, async onChange (values) { - if(selectElementI){ + const selectElementI = selectElement ? document.getElementById(selectElement) : null; + if (selectElementI){ while (selectElementI.options.length > 0) { selectElementI.remove(0); } diff --git a/BTCPayServer/wwwroot/vendor/tom-select/tom-select.bootstrap5.min.css b/BTCPayServer/wwwroot/vendor/tom-select/tom-select.bootstrap5.min.css index c63827fd7..4b67ec729 100644 --- a/BTCPayServer/wwwroot/vendor/tom-select/tom-select.bootstrap5.min.css +++ b/BTCPayServer/wwwroot/vendor/tom-select/tom-select.bootstrap5.min.css @@ -1,2 +1,2 @@ :root{--ts-pr-clear-button:0;--ts-pr-caret:0;--ts-pr-min:.75rem}.ts-wrapper.single .ts-control,.ts-wrapper.single .ts-control input{cursor:pointer}.ts-control{padding-right:max(var(--ts-pr-min),var(--ts-pr-clear-button) + var(--ts-pr-caret))!important}.ts-wrapper.plugin-drag_drop.multi>.ts-control>div.ui-sortable-placeholder{background:#f2f2f2!important;background:rgba(0,0,0,.06)!important;border:0!important;box-shadow:inset 0 0 12px 4px #fff;visibility:visible!important}.ts-wrapper.plugin-drag_drop .ui-sortable-placeholder:after{content:"!";visibility:hidden}.ts-wrapper.plugin-drag_drop .ui-sortable-helper{box-shadow:0 2px 5px rgba(0,0,0,.2)}.plugin-checkbox_options .option input{margin-right:.5rem}.plugin-clear_button{--ts-pr-clear-button:1em}.plugin-clear_button .clear-button{background:transparent!important;cursor:pointer;margin-right:0!important;opacity:0;position:absolute;right:calc(.75rem - 5px);top:50%;transform:translateY(-50%);transition:opacity .5s}.plugin-clear_button.form-select .clear-button,.plugin-clear_button.single .clear-button{right:max(var(--ts-pr-caret),.75rem)}.plugin-clear_button.focus.has-items .clear-button,.plugin-clear_button:not(.disabled):hover.has-items .clear-button{opacity:1}.ts-wrapper .dropdown-header{background:#f8f8f8;border-bottom:1px solid #d0d0d0;border-radius:.375rem .375rem 0 0;padding:6px .75rem;position:relative}.ts-wrapper .dropdown-header-close{color:#343a40;font-size:20px!important;line-height:20px;margin-top:-12px;opacity:.4;position:absolute;right:.75rem;top:50%}.ts-wrapper .dropdown-header-close:hover{color:#000}.plugin-dropdown_input.focus.dropdown-active .ts-control{border:1px solid #ced4da;box-shadow:none;box-shadow:inset 0 1px 2px rgba(0,0,0,.075)}.plugin-dropdown_input .dropdown-input{background:transparent;border:solid #d0d0d0;border-width:0 0 1px;box-shadow:none;display:block;padding:.375rem .75rem;width:100%}.plugin-dropdown_input.focus .ts-dropdown .dropdown-input{border-color:#86b7fe;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);outline:0}.plugin-dropdown_input .items-placeholder{border:0!important;box-shadow:none!important;width:100%}.plugin-dropdown_input.dropdown-active .items-placeholder,.plugin-dropdown_input.has-items .items-placeholder{display:none!important}.ts-wrapper.plugin-input_autogrow.has-items .ts-control>input{min-width:0}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input{flex:none;min-width:4px}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input::-ms-input-placeholder{color:transparent}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input::placeholder{color:transparent}.ts-dropdown.plugin-optgroup_columns .ts-dropdown-content{display:flex}.ts-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0;flex-basis:0;flex-grow:1;min-width:0}.ts-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0}.ts-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.ts-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0}.ts-wrapper.plugin-remove_button .item{align-items:center;display:inline-flex;padding-right:0!important}.ts-wrapper.plugin-remove_button .item .remove{border-radius:0 2px 2px 0;box-sizing:border-box;color:inherit;display:inline-block;padding:0 5px;text-decoration:none;vertical-align:middle}.ts-wrapper.plugin-remove_button .item .remove:hover{background:rgba(0,0,0,.05)}.ts-wrapper.plugin-remove_button.disabled .item .remove:hover{background:none}.ts-wrapper.plugin-remove_button .remove-single{font-size:23px;position:absolute;right:0;top:0}.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove{border-left:1px solid #dee2e6;margin-left:5px}.ts-wrapper.plugin-remove_button:not(.rtl) .item.active .remove{border-left-color:transparent}.ts-wrapper.plugin-remove_button:not(.rtl).disabled .item .remove{border-left-color:#fff}.ts-wrapper.plugin-remove_button.rtl .item .remove{border-right:1px solid #dee2e6;margin-right:5px}.ts-wrapper.plugin-remove_button.rtl .item.active .remove{border-right-color:transparent}.ts-wrapper.plugin-remove_button.rtl.disabled .item .remove{border-right-color:#fff}.ts-wrapper{position:relative}.ts-control,.ts-control input,.ts-dropdown{font-smoothing:inherit;color:#343a40;font-family:inherit;font-size:inherit;line-height:1.5}.ts-control,.ts-wrapper.single.input-active .ts-control{background:#fff;cursor:text}.ts-control{border:1px solid #ced4da;border-radius:.375rem;box-shadow:none;box-sizing:border-box;flex-wrap:wrap;overflow:hidden;padding:.375rem .75rem;position:relative;width:100%;z-index:1}.ts-wrapper.multi.has-items .ts-control{padding:calc(.375rem - 1px) .75rem calc(.375rem - 4px)}.full .ts-control{background-color:#fff}.disabled .ts-control,.disabled .ts-control *{cursor:default!important}.focus .ts-control{box-shadow:none}.ts-control>*{display:inline-block;vertical-align:baseline}.ts-wrapper.multi .ts-control>div{background:#efefef;border:0 solid #dee2e6;color:#343a40;cursor:pointer;margin:0 3px 3px 0;padding:1px 5px}.ts-wrapper.multi .ts-control>div.active{background:#0d6efd;border:0 solid transparent;color:#fff}.ts-wrapper.multi.disabled .ts-control>div,.ts-wrapper.multi.disabled .ts-control>div.active{background:#fff;border:0 solid #fff;color:#878787}.ts-control>input{background:none!important;border:0!important;box-shadow:none!important;display:inline-block!important;flex:1 1 auto;line-height:inherit!important;margin:0!important;max-height:none!important;max-width:100%!important;min-height:0!important;min-width:7rem;padding:0!important;text-indent:0!important;-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.ts-control>input::-ms-clear{display:none}.ts-control>input:focus{outline:none!important}.has-items .ts-control>input{margin:0 4px!important}.ts-control.rtl{text-align:right}.ts-control.rtl.single .ts-control:after{left:calc(.75rem + 5px);right:auto}.ts-control.rtl .ts-control>input{margin:0 4px 0 -2px!important}.disabled .ts-control{background-color:#e9ecef;opacity:.5}.input-hidden .ts-control>input{left:-10000px;opacity:0;position:absolute}.ts-dropdown{background:#fff;border:1px solid #d0d0d0;border-radius:0 0 .375rem .375rem;border-top:0;box-shadow:0 1px 3px rgba(0,0,0,.1);box-sizing:border-box;left:0;margin:.25rem 0 0;position:absolute;top:100%;width:100%;z-index:10}.ts-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.ts-dropdown [data-selectable] .highlight{background:rgba(255,237,40,.4);border-radius:1px}.ts-dropdown .create,.ts-dropdown .no-results,.ts-dropdown .optgroup-header,.ts-dropdown .option{padding:3px .75rem}.ts-dropdown .option,.ts-dropdown [data-disabled],.ts-dropdown [data-disabled] [data-selectable].option{cursor:inherit;opacity:.5}.ts-dropdown [data-selectable].option{cursor:pointer;opacity:1}.ts-dropdown .optgroup:first-child .optgroup-header{border-top:0}.ts-dropdown .optgroup-header{background:#fff;color:#6c757d;cursor:default}.ts-dropdown .active{background-color:#e9ecef;color:#1e2125}.ts-dropdown .active.create{color:#1e2125}.ts-dropdown .create{color:rgba(52,58,64,.5)}.ts-dropdown .spinner{display:inline-block;height:30px;margin:3px .75rem;width:30px}.ts-dropdown .spinner:after{animation:lds-dual-ring 1.2s linear infinite;border-color:#d0d0d0 transparent;border-radius:50%;border-style:solid;border-width:5px;content:" ";display:block;height:24px;margin:3px;width:24px}@keyframes lds-dual-ring{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.ts-dropdown-content{overflow-scrolling:touch;max-height:200px;overflow-x:hidden;overflow-y:auto;scroll-behavior:smooth}.ts-hidden-accessible{clip:rect(0 0 0 0)!important;border:0!important;-webkit-clip-path:inset(50%)!important;clip-path:inset(50%)!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}.ts-wrapper.form-control,.ts-wrapper.form-select{box-shadow:none;display:flex;height:auto;padding:0!important}.ts-dropdown,.ts-dropdown.form-control,.ts-dropdown.form-select{background:#fff;border:1px solid var(--bs-border-color-translucent);border-radius:.375rem;box-shadow:0 6px 12px rgba(0,0,0,.175);height:auto;padding:0;z-index:1000}.ts-dropdown .optgroup-header{font-size:.875rem;line-height:1.5}.ts-dropdown .optgroup:first-child:before{display:none}.ts-dropdown .optgroup:before{border-top:1px solid var(--bs-border-color-translucent);content:" ";display:block;height:0;margin:.5rem -.75rem;overflow:hidden}.ts-dropdown .create{padding-left:.75rem}.ts-dropdown-content{padding:5px 0}.ts-control{align-items:center;display:flex;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.ts-control{transition:none}}.ts-control.dropdown -active{border-radius:.375rem}.focus .ts-control{border-color:#86b7fe;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);outline:0}.ts-control .item{align-items:center;display:flex}.ts-wrapper.is-invalid,.was-validated .invalid,.was-validated :invalid+.ts-wrapper{border-color:#dc3545}.ts-wrapper.is-invalid:not(.single),.was-validated .invalid:not(.single),.was-validated :invalid+.ts-wrapper:not(.single){background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E");background-position:right calc(.375em + .1875rem) center;background-repeat:no-repeat;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.ts-wrapper.is-invalid.single,.was-validated .invalid.single,.was-validated :invalid+.ts-wrapper.single{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3E%3C/svg%3E"),url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E");background-position:right .75rem center,center right 2.25rem;background-repeat:no-repeat;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.ts-wrapper.is-invalid.focus .ts-control,.was-validated .invalid.focus .ts-control,.was-validated :invalid+.ts-wrapper.focus .ts-control{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.ts-wrapper.is-valid,.was-validated .valid,.was-validated :valid+.ts-wrapper{border-color:#198754}.ts-wrapper.is-valid:not(.single),.was-validated .valid:not(.single),.was-validated :valid+.ts-wrapper:not(.single){background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E");background-position:right calc(.375em + .1875rem) center;background-repeat:no-repeat;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.ts-wrapper.is-valid.single,.was-validated .valid.single,.was-validated :valid+.ts-wrapper.single{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3E%3C/svg%3E"),url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E");background-position:right .75rem center,center right 2.25rem;background-repeat:no-repeat;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.ts-wrapper.is-valid.focus .ts-control,.was-validated .valid.focus .ts-control,.was-validated :valid+.ts-wrapper.focus .ts-control{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.ts-wrapper{display:flex;min-height:calc(1.5em + .75rem + 2px)}.input-group-sm>.ts-wrapper,.ts-wrapper.form-control-sm,.ts-wrapper.form-select-sm{min-height:calc(1.5em + .5rem + 2px)}.input-group-sm>.ts-wrapper .ts-control,.ts-wrapper.form-control-sm .ts-control,.ts-wrapper.form-select-sm .ts-control{border-radius:.25rem;font-size:.875rem}.input-group-sm>.ts-wrapper.has-items .ts-control,.ts-wrapper.form-control-sm.has-items .ts-control,.ts-wrapper.form-select-sm.has-items .ts-control{font-size:.875rem;padding-bottom:0}.input-group-sm>.ts-wrapper.multi.has-items .ts-control,.ts-wrapper.form-control-sm.multi.has-items .ts-control,.ts-wrapper.form-select-sm.multi.has-items .ts-control{padding-top:calc(.75em - .40625rem - 1px)!important}.ts-wrapper.multi.has-items .ts-control{--ts-pr-min:calc(0.75rem - 5px);padding-left:calc(.75rem - 5px)}.ts-wrapper.multi .ts-control>div{border-radius:calc(.375rem - 1px)}.input-group-lg>.ts-wrapper,.ts-wrapper.form-control-lg,.ts-wrapper.form-select-lg{min-height:calc(1.5em + 1rem + 2px)}.input-group-lg>.ts-wrapper .ts-control,.ts-wrapper.form-control-lg .ts-control,.ts-wrapper.form-select-lg .ts-control{border-radius:.5rem;font-size:1.25rem}.ts-wrapper:not(.form-control):not(.form-select){background:none;border:none;box-shadow:none;height:auto;padding:0}.ts-wrapper:not(.form-control):not(.form-select).single .ts-control{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3E%3C/svg%3E");background-position:right .75rem center;background-repeat:no-repeat;background-size:16px 12px}.ts-wrapper.form-select,.ts-wrapper.single{--ts-pr-caret:2.25rem}.ts-wrapper.form-control .ts-control,.ts-wrapper.form-control.single.input-active .ts-control,.ts-wrapper.form-select .ts-control,.ts-wrapper.form-select.single.input-active .ts-control{border:none!important}.ts-wrapper.form-control:not(.disabled) .ts-control,.ts-wrapper.form-control:not(.disabled).single.input-active .ts-control,.ts-wrapper.form-select:not(.disabled) .ts-control,.ts-wrapper.form-select:not(.disabled).single.input-active .ts-control{background:transparent!important}.input-group>.ts-wrapper{flex-grow:1}.input-group>.ts-wrapper:not(:nth-child(2))>.ts-control{border-bottom-left-radius:0;border-top-left-radius:0}.input-group>.ts-wrapper:not(:last-child)>.ts-control{border-bottom-right-radius:0;border-top-right-radius:0} -/*# sourceMappingURL=tom-select.bootstrap5.min.css.map */ \ No newline at end of file +/*# sourceMappingURL=tom-select.bootstrap5.min.css.map */ diff --git a/BTCPayServer/wwwroot/vendor/tom-select/tom-select.bootstrap5.min.css.map b/BTCPayServer/wwwroot/vendor/tom-select/tom-select.bootstrap5.min.css.map new file mode 100644 index 000000000..55c12d403 --- /dev/null +++ b/BTCPayServer/wwwroot/vendor/tom-select/tom-select.bootstrap5.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["tom-select.bootstrap5.css"],"names":[],"mappings":"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","file":"tom-select.bootstrap5.min.css","sourcesContent":["/**\n * Tom Select bootstrap 5\n */\n/**\n * tom-select.css (v//@@version)\n * Copyright (c) contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this\n * file except in compliance with the License. You may obtain a copy of the License at:\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n * ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n *\n */\n:root {\n --ts-pr-clear-button:\t0;\n --ts-pr-caret:\t0;\n --ts-pr-min:\t.75rem;\n}\n\n.ts-wrapper.single .ts-control, .ts-wrapper.single .ts-control input {\n cursor: pointer;\n}\n\n.ts-control {\n padding-right: max(var(--ts-pr-min), var(--ts-pr-clear-button) + var(--ts-pr-caret)) !important;\n}\n\n.ts-wrapper.plugin-drag_drop.multi > .ts-control > div.ui-sortable-placeholder {\n visibility: visible !important;\n background: #f2f2f2 !important;\n background: rgba(0, 0, 0, 0.06) !important;\n border: 0 none !important;\n box-shadow: inset 0 0 12px 4px #fff;\n}\n.ts-wrapper.plugin-drag_drop .ui-sortable-placeholder::after {\n content: \"!\";\n visibility: hidden;\n}\n.ts-wrapper.plugin-drag_drop .ui-sortable-helper {\n box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);\n}\n\n.plugin-checkbox_options .option input {\n margin-right: 0.5rem;\n}\n\n.plugin-clear_button {\n --ts-pr-clear-button: 1em;\n}\n.plugin-clear_button .clear-button {\n opacity: 0;\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n right: calc(0.75rem - 5px);\n margin-right: 0 !important;\n background: transparent !important;\n transition: opacity 0.5s;\n cursor: pointer;\n}\n.plugin-clear_button.form-select .clear-button, .plugin-clear_button.single .clear-button {\n right: max( var(--ts-pr-caret), 0.75rem);\n}\n.plugin-clear_button.focus.has-items .clear-button, .plugin-clear_button:not(.disabled):hover.has-items .clear-button {\n opacity: 1;\n}\n\n.ts-wrapper .dropdown-header {\n position: relative;\n padding: 6px 0.75rem;\n border-bottom: 1px solid #d0d0d0;\n background: #f8f8f8;\n border-radius: 0.375rem 0.375rem 0 0;\n}\n.ts-wrapper .dropdown-header-close {\n position: absolute;\n right: 0.75rem;\n top: 50%;\n color: #343a40;\n opacity: 0.4;\n margin-top: -12px;\n line-height: 20px;\n font-size: 20px !important;\n}\n.ts-wrapper .dropdown-header-close:hover {\n color: black;\n}\n\n.plugin-dropdown_input.focus.dropdown-active .ts-control {\n box-shadow: none;\n border: 1px solid #ced4da;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.plugin-dropdown_input .dropdown-input {\n border: 1px solid #d0d0d0;\n border-width: 0 0 1px 0;\n display: block;\n padding: 0.375rem 0.75rem;\n box-shadow: none;\n width: 100%;\n background: transparent;\n}\n.plugin-dropdown_input.focus .ts-dropdown .dropdown-input {\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.plugin-dropdown_input .items-placeholder {\n border: 0 none !important;\n box-shadow: none !important;\n width: 100%;\n}\n.plugin-dropdown_input.has-items .items-placeholder, .plugin-dropdown_input.dropdown-active .items-placeholder {\n display: none !important;\n}\n\n.ts-wrapper.plugin-input_autogrow.has-items .ts-control > input {\n min-width: 0;\n}\n.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control > input {\n flex: none;\n min-width: 4px;\n}\n.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control > input::placeholder {\n color: transparent;\n}\n\n.ts-dropdown.plugin-optgroup_columns .ts-dropdown-content {\n display: flex;\n}\n.ts-dropdown.plugin-optgroup_columns .optgroup {\n border-right: 1px solid #f2f2f2;\n border-top: 0 none;\n flex-grow: 1;\n flex-basis: 0;\n min-width: 0;\n}\n.ts-dropdown.plugin-optgroup_columns .optgroup:last-child {\n border-right: 0 none;\n}\n.ts-dropdown.plugin-optgroup_columns .optgroup:before {\n display: none;\n}\n.ts-dropdown.plugin-optgroup_columns .optgroup-header {\n border-top: 0 none;\n}\n\n.ts-wrapper.plugin-remove_button .item {\n display: inline-flex;\n align-items: center;\n padding-right: 0 !important;\n}\n.ts-wrapper.plugin-remove_button .item .remove {\n color: inherit;\n text-decoration: none;\n vertical-align: middle;\n display: inline-block;\n padding: 0 5px;\n border-radius: 0 2px 2px 0;\n box-sizing: border-box;\n}\n.ts-wrapper.plugin-remove_button .item .remove:hover {\n background: rgba(0, 0, 0, 0.05);\n}\n.ts-wrapper.plugin-remove_button.disabled .item .remove:hover {\n background: none;\n}\n.ts-wrapper.plugin-remove_button .remove-single {\n position: absolute;\n right: 0;\n top: 0;\n font-size: 23px;\n}\n\n.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove {\n border-left: 1px solid #dee2e6;\n margin-left: 5px;\n}\n.ts-wrapper.plugin-remove_button:not(.rtl) .item.active .remove {\n border-left-color: rgba(0, 0, 0, 0);\n}\n.ts-wrapper.plugin-remove_button:not(.rtl).disabled .item .remove {\n border-left-color: white;\n}\n\n.ts-wrapper.plugin-remove_button.rtl .item .remove {\n border-right: 1px solid #dee2e6;\n margin-right: 5px;\n}\n.ts-wrapper.plugin-remove_button.rtl .item.active .remove {\n border-right-color: rgba(0, 0, 0, 0);\n}\n.ts-wrapper.plugin-remove_button.rtl.disabled .item .remove {\n border-right-color: white;\n}\n\n.ts-wrapper {\n position: relative;\n}\n\n.ts-dropdown,\n.ts-control,\n.ts-control input {\n color: #343a40;\n font-family: inherit;\n font-size: inherit;\n line-height: 1.5;\n font-smoothing: inherit;\n}\n\n.ts-control,\n.ts-wrapper.single.input-active .ts-control {\n background: #fff;\n cursor: text;\n}\n\n.ts-control {\n border: 1px solid #ced4da;\n padding: 0.375rem 0.75rem;\n width: 100%;\n overflow: hidden;\n position: relative;\n z-index: 1;\n box-sizing: border-box;\n box-shadow: none;\n border-radius: 0.375rem;\n display: flex;\n flex-wrap: wrap;\n}\n.ts-wrapper.multi.has-items .ts-control {\n padding: calc( 0.375rem - 1px - 0px) 0.75rem calc( 0.375rem - 1px - 3px - 0px);\n}\n.full .ts-control {\n background-color: #fff;\n}\n.disabled .ts-control, .disabled .ts-control * {\n cursor: default !important;\n}\n.focus .ts-control {\n box-shadow: none;\n}\n.ts-control > * {\n vertical-align: baseline;\n display: inline-block;\n}\n.ts-wrapper.multi .ts-control > div {\n cursor: pointer;\n margin: 0 3px 3px 0;\n padding: 1px 5px;\n background: #efefef;\n color: #343a40;\n border: 0px solid #dee2e6;\n}\n.ts-wrapper.multi .ts-control > div.active {\n background: #0d6efd;\n color: #fff;\n border: 0px solid rgba(0, 0, 0, 0);\n}\n.ts-wrapper.multi.disabled .ts-control > div, .ts-wrapper.multi.disabled .ts-control > div.active {\n color: #878787;\n background: white;\n border: 0px solid white;\n}\n.ts-control > input {\n flex: 1 1 auto;\n min-width: 7rem;\n display: inline-block !important;\n padding: 0 !important;\n min-height: 0 !important;\n max-height: none !important;\n max-width: 100% !important;\n margin: 0 !important;\n text-indent: 0 !important;\n border: 0 none !important;\n background: none !important;\n line-height: inherit !important;\n user-select: auto !important;\n box-shadow: none !important;\n}\n.ts-control > input::-ms-clear {\n display: none;\n}\n.ts-control > input:focus {\n outline: none !important;\n}\n.has-items .ts-control > input {\n margin: 0px 4px !important;\n}\n.ts-control.rtl {\n text-align: right;\n}\n.ts-control.rtl.single .ts-control:after {\n left: calc(0.75rem + 5px);\n right: auto;\n}\n.ts-control.rtl .ts-control > input {\n margin: 0px 4px 0px -2px !important;\n}\n.disabled .ts-control {\n opacity: 0.5;\n background-color: #e9ecef;\n}\n.input-hidden .ts-control > input {\n opacity: 0;\n position: absolute;\n left: -10000px;\n}\n\n.ts-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n width: 100%;\n z-index: 10;\n border: 1px solid #d0d0d0;\n background: #fff;\n margin: 0.25rem 0 0 0;\n border-top: 0 none;\n box-sizing: border-box;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n border-radius: 0 0 0.375rem 0.375rem;\n}\n.ts-dropdown [data-selectable] {\n cursor: pointer;\n overflow: hidden;\n}\n.ts-dropdown [data-selectable] .highlight {\n background: rgba(255, 237, 40, 0.4);\n border-radius: 1px;\n}\n.ts-dropdown .option,\n.ts-dropdown .optgroup-header,\n.ts-dropdown .no-results,\n.ts-dropdown .create {\n padding: 3px 0.75rem;\n}\n.ts-dropdown .option, .ts-dropdown [data-disabled], .ts-dropdown [data-disabled] [data-selectable].option {\n cursor: inherit;\n opacity: 0.5;\n}\n.ts-dropdown [data-selectable].option {\n opacity: 1;\n cursor: pointer;\n}\n.ts-dropdown .optgroup:first-child .optgroup-header {\n border-top: 0 none;\n}\n.ts-dropdown .optgroup-header {\n color: #6c757d;\n background: #fff;\n cursor: default;\n}\n.ts-dropdown .active {\n background-color: #e9ecef;\n color: #1e2125;\n}\n.ts-dropdown .active.create {\n color: #1e2125;\n}\n.ts-dropdown .create {\n color: rgba(52, 58, 64, 0.5);\n}\n.ts-dropdown .spinner {\n display: inline-block;\n width: 30px;\n height: 30px;\n margin: 3px 0.75rem;\n}\n.ts-dropdown .spinner:after {\n content: \" \";\n display: block;\n width: 24px;\n height: 24px;\n margin: 3px;\n border-radius: 50%;\n border: 5px solid #d0d0d0;\n border-color: #d0d0d0 transparent #d0d0d0 transparent;\n animation: lds-dual-ring 1.2s linear infinite;\n}\n@keyframes lds-dual-ring {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n\n.ts-dropdown-content {\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 200px;\n overflow-scrolling: touch;\n scroll-behavior: smooth;\n}\n\n.ts-hidden-accessible {\n border: 0 !important;\n clip: rect(0 0 0 0) !important;\n clip-path: inset(50%) !important;\n overflow: hidden !important;\n padding: 0 !important;\n position: absolute !important;\n width: 1px !important;\n white-space: nowrap !important;\n}\n\n.ts-wrapper.form-control,\n.ts-wrapper.form-select {\n padding: 0 !important;\n height: auto;\n box-shadow: none;\n display: flex;\n}\n\n.ts-dropdown,\n.ts-dropdown.form-control,\n.ts-dropdown.form-select {\n height: auto;\n padding: 0;\n z-index: 1000;\n background: #fff;\n border: 1px solid var(--bs-border-color-translucent);\n border-radius: 0.375rem;\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n}\n\n.ts-dropdown .optgroup-header {\n font-size: 0.875rem;\n line-height: 1.5;\n}\n.ts-dropdown .optgroup:first-child:before {\n display: none;\n}\n.ts-dropdown .optgroup:before {\n content: \" \";\n display: block;\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid var(--bs-border-color-translucent);\n margin-left: -0.75rem;\n margin-right: -0.75rem;\n}\n.ts-dropdown .create {\n padding-left: 0.75rem;\n}\n\n.ts-dropdown-content {\n padding: 5px 0;\n}\n\n.ts-control {\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n display: flex;\n align-items: center;\n}\n@media (prefers-reduced-motion: reduce) {\n .ts-control {\n transition: none;\n }\n}\n.ts-control.dropdown -active {\n border-radius: 0.375rem;\n}\n.focus .ts-control {\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.ts-control .item {\n display: flex;\n align-items: center;\n}\n\n.ts-wrapper.is-invalid,\n.was-validated .invalid,\n.was-validated :invalid + .ts-wrapper {\n border-color: #dc3545;\n}\n.ts-wrapper.is-invalid:not(.single),\n.was-validated .invalid:not(.single),\n.was-validated :invalid + .ts-wrapper:not(.single) {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n background-position: right calc(0.375em + 0.1875rem) center;\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n background-repeat: no-repeat;\n}\n.ts-wrapper.is-invalid.single,\n.was-validated .invalid.single,\n.was-validated :invalid + .ts-wrapper.single {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\"), url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n background-position: right 0.75rem center, center right 2.25rem;\n background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n background-repeat: no-repeat;\n}\n.ts-wrapper.is-invalid.focus .ts-control,\n.was-validated .invalid.focus .ts-control,\n.was-validated :invalid + .ts-wrapper.focus .ts-control {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n\n.ts-wrapper.is-valid,\n.was-validated .valid,\n.was-validated :valid + .ts-wrapper {\n border-color: #198754;\n}\n.ts-wrapper.is-valid:not(.single),\n.was-validated .valid:not(.single),\n.was-validated :valid + .ts-wrapper:not(.single) {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n background-position: right calc(0.375em + 0.1875rem) center;\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n background-repeat: no-repeat;\n}\n.ts-wrapper.is-valid.single,\n.was-validated .valid.single,\n.was-validated :valid + .ts-wrapper.single {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\"), url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n background-position: right 0.75rem center, center right 2.25rem;\n background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n background-repeat: no-repeat;\n}\n.ts-wrapper.is-valid.focus .ts-control,\n.was-validated .valid.focus .ts-control,\n.was-validated :valid + .ts-wrapper.focus .ts-control {\n border-color: #198754;\n box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n\n.ts-wrapper {\n min-height: calc(1.5em + 0.75rem + 2px);\n display: flex;\n}\n.input-group-sm > .ts-wrapper, .ts-wrapper.form-select-sm, .ts-wrapper.form-control-sm {\n min-height: calc(1.5em + 0.5rem + 2px);\n}\n.input-group-sm > .ts-wrapper .ts-control, .ts-wrapper.form-select-sm .ts-control, .ts-wrapper.form-control-sm .ts-control {\n border-radius: 0.25rem;\n font-size: 0.875rem;\n}\n.input-group-sm > .ts-wrapper.has-items .ts-control, .ts-wrapper.form-select-sm.has-items .ts-control, .ts-wrapper.form-control-sm.has-items .ts-control {\n font-size: 0.875rem;\n padding-bottom: 0;\n}\n.input-group-sm > .ts-wrapper.multi.has-items .ts-control, .ts-wrapper.form-select-sm.multi.has-items .ts-control, .ts-wrapper.form-control-sm.multi.has-items .ts-control {\n padding-top: calc((calc(1.5em + 0.5rem + 2px) - (1.5 * 0.875rem) - 4px) / 2) !important;\n}\n.ts-wrapper.multi.has-items .ts-control {\n padding-left: calc(0.75rem - 5px);\n --ts-pr-min: calc(0.75rem - 5px);\n}\n.ts-wrapper.multi .ts-control > div {\n border-radius: calc(0.375rem - 1px);\n}\n.input-group-lg > .ts-wrapper, .ts-wrapper.form-control-lg, .ts-wrapper.form-select-lg {\n min-height: calc(1.5em + 1rem + 2px);\n}\n.input-group-lg > .ts-wrapper .ts-control, .ts-wrapper.form-control-lg .ts-control, .ts-wrapper.form-select-lg .ts-control {\n border-radius: 0.5rem;\n font-size: 1.25rem;\n}\n\n.ts-wrapper:not(.form-control):not(.form-select) {\n padding: 0;\n border: none;\n height: auto;\n box-shadow: none;\n background: none;\n}\n.ts-wrapper:not(.form-control):not(.form-select).single .ts-control {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: right 0.75rem center;\n background-size: 16px 12px;\n}\n.ts-wrapper.form-select, .ts-wrapper.single {\n --ts-pr-caret: 2.25rem;\n}\n\n.ts-wrapper.form-control .ts-control, .ts-wrapper.form-control.single.input-active .ts-control,\n.ts-wrapper.form-select .ts-control,\n.ts-wrapper.form-select.single.input-active .ts-control {\n border: none !important;\n}\n.ts-wrapper.form-control:not(.disabled) .ts-control, .ts-wrapper.form-control:not(.disabled).single.input-active .ts-control,\n.ts-wrapper.form-select:not(.disabled) .ts-control,\n.ts-wrapper.form-select:not(.disabled).single.input-active .ts-control {\n background: transparent !important;\n}\n\n.input-group > .ts-wrapper {\n flex-grow: 1;\n}\n.input-group > .ts-wrapper:not(:nth-child(2)) > .ts-control {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n.input-group > .ts-wrapper:not(:last-child) > .ts-control {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}"]} \ No newline at end of file diff --git a/BTCPayServer/wwwroot/vendor/tom-select/tom-select.complete.min.js.map b/BTCPayServer/wwwroot/vendor/tom-select/tom-select.complete.min.js.map new file mode 100644 index 000000000..1f834431b --- /dev/null +++ b/BTCPayServer/wwwroot/vendor/tom-select/tom-select.complete.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tom-select.complete.min.js","sources":["../../src/contrib/microevent.ts","../../node_modules/@orchidjs/unicode-variants/dist/esm/regex.js","../../node_modules/@orchidjs/unicode-variants/dist/esm/strings.js","../../node_modules/@orchidjs/unicode-variants/dist/esm/index.js","../../node_modules/@orchidjs/sifter/dist/esm/utils.js","../../node_modules/@orchidjs/sifter/dist/esm/sifter.js","../../node_modules/@orchidjs/sifter/lib/utils.ts","../../src/vanilla.ts","../../src/contrib/highlight.ts","../../src/constants.ts","../../src/defaults.ts","../../src/utils.ts","../../src/getSettings.ts","../../src/tom-select.ts","../../src/contrib/microplugin.ts","../../src/tom-select.complete.ts","../../src/plugins/change_listener/plugin.ts","../../src/plugins/checkbox_options/plugin.ts","../../src/plugins/clear_button/plugin.ts","../../src/plugins/drag_drop/plugin.ts","../../src/plugins/dropdown_header/plugin.ts","../../src/plugins/caret_position/plugin.ts","../../src/plugins/dropdown_input/plugin.ts","../../src/plugins/input_autogrow/plugin.ts","../../src/plugins/no_backspace_delete/plugin.ts","../../src/plugins/no_active_items/plugin.ts","../../src/plugins/optgroup_columns/plugin.ts","../../src/plugins/remove_button/plugin.ts","../../src/plugins/restore_on_backspace/plugin.ts","../../src/plugins/virtual_scroll/plugin.ts"],"sourcesContent":["/**\n * MicroEvent - to make any js object an event emitter\n *\n * - pure javascript - server compatible, browser compatible\n * - dont rely on the browser doms\n * - super simple - you get it immediatly, no mistery, no magic involved\n *\n * @author Jerome Etienne (https://github.com/jeromeetienne)\n */\n\ntype TCallback = (...args:any) => any;\n\n/**\n * Execute callback for each event in space separated list of event names\n *\n */\nfunction forEvents(events:string,callback:(event:string)=>any){\n\tevents.split(/\\s+/).forEach((event) =>{\n\t\tcallback(event);\n\t});\n}\n\nexport default class MicroEvent{\n\n\tpublic _events: {[key:string]:TCallback[]};\n\n\tconstructor(){\n\t\tthis._events = {};\n\t}\n\n\ton(events:string, fct:TCallback){\n\t\tforEvents(events,(event) => {\n\t\t\tconst event_array = this._events[event] || [];\n\t\t\tevent_array.push(fct);\n\t\t\tthis._events[event] = event_array;\n\t\t});\n\t}\n\n\toff(events:string, fct:TCallback){\n\t\tvar n = arguments.length;\n\t\tif( n === 0 ){\n\t\t\tthis._events = {};\n\t\t\treturn;\n\t\t}\n\n\t\tforEvents(events,(event) => {\n\n\t\t\tif (n === 1){\n\t\t\t\tdelete this._events[event];\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst event_array = this._events[event];\n\t\t\tif( event_array === undefined ) return;\n\n\t\t\tevent_array.splice(event_array.indexOf(fct), 1);\n\t\t\tthis._events[event] = event_array;\n\t\t});\n\t}\n\n\ttrigger(events:string, ...args:any){\n\t\tvar self = this;\n\n\t\tforEvents(events,(event) => {\n\t\t\tconst event_array = self._events[event];\n\t\t\tif( event_array === undefined ) return;\n\t\t\tevent_array.forEach(fct => {\n\t\t\t\tfct.apply(self, args );\n\t\t\t});\n\n\t\t});\n\t}\n};\n","/*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */\n/**\n * Convert array of strings to a regular expression\n *\tex ['ab','a'] => (?:ab|a)\n * \tex ['a','b'] => [ab]\n * @param {string[]} chars\n * @return {string}\n */\nconst arrayToPattern = chars => {\n chars = chars.filter(Boolean);\n\n if (chars.length < 2) {\n return chars[0] || '';\n }\n\n return maxValueLength(chars) == 1 ? '[' + chars.join('') + ']' : '(?:' + chars.join('|') + ')';\n};\n/**\n * @param {string[]} array\n * @return {string}\n */\n\nconst sequencePattern = array => {\n if (!hasDuplicates(array)) {\n return array.join('');\n }\n\n let pattern = '';\n let prev_char_count = 0;\n\n const prev_pattern = () => {\n if (prev_char_count > 1) {\n pattern += '{' + prev_char_count + '}';\n }\n };\n\n array.forEach((char, i) => {\n if (char === array[i - 1]) {\n prev_char_count++;\n return;\n }\n\n prev_pattern();\n pattern += char;\n prev_char_count = 1;\n });\n prev_pattern();\n return pattern;\n};\n/**\n * Convert array of strings to a regular expression\n *\tex ['ab','a'] => (?:ab|a)\n * \tex ['a','b'] => [ab]\n * @param {Set} chars\n * @return {string}\n */\n\nconst setToPattern = chars => {\n let array = toArray(chars);\n return arrayToPattern(array);\n};\n/**\n *\n * https://stackoverflow.com/questions/7376598/in-javascript-how-do-i-check-if-an-array-has-duplicate-values\n * @param {any[]} array\n */\n\nconst hasDuplicates = array => {\n return new Set(array).size !== array.length;\n};\n/**\n * https://stackoverflow.com/questions/63006601/why-does-u-throw-an-invalid-escape-error\n * @param {string} str\n * @return {string}\n */\n\nconst escape_regex = str => {\n return (str + '').replace(/([\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\|\\}\\\\])/gu, '\\\\$1');\n};\n/**\n * Return the max length of array values\n * @param {string[]} array\n *\n */\n\nconst maxValueLength = array => {\n return array.reduce((longest, value) => Math.max(longest, unicodeLength(value)), 0);\n};\n/**\n * @param {string} str\n */\n\nconst unicodeLength = str => {\n return toArray(str).length;\n};\n/**\n * @param {any} p\n * @return {any[]}\n */\n\nconst toArray = p => Array.from(p);\n\nexport { arrayToPattern, escape_regex, hasDuplicates, maxValueLength, sequencePattern, setToPattern, toArray, unicodeLength };\n//# sourceMappingURL=regex.js.map\n","/*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */\n/**\n * Get all possible combinations of substrings that add up to the given string\n * https://stackoverflow.com/questions/30169587/find-all-the-combination-of-substrings-that-add-up-to-the-given-string\n * @param {string} input\n * @return {string[][]}\n */\nconst allSubstrings = input => {\n if (input.length === 1) return [[input]];\n /** @type {string[][]} */\n\n let result = [];\n const start = input.substring(1);\n const suba = allSubstrings(start);\n suba.forEach(function (subresult) {\n let tmp = subresult.slice(0);\n tmp[0] = input.charAt(0) + tmp[0];\n result.push(tmp);\n tmp = subresult.slice(0);\n tmp.unshift(input.charAt(0));\n result.push(tmp);\n });\n return result;\n};\n\nexport { allSubstrings };\n//# sourceMappingURL=strings.js.map\n","/*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */\nimport { toArray, setToPattern, escape_regex, arrayToPattern, sequencePattern } from './regex.js';\nexport { escape_regex } from './regex.js';\nimport { allSubstrings } from './strings.js';\n\n/**\n * @typedef {{[key:string]:string}} TUnicodeMap\n * @typedef {{[key:string]:Set}} TUnicodeSets\n * @typedef {[[number,number]]} TCodePoints\n * @typedef {{folded:string,composed:string,code_point:number}} TCodePointObj\n * @typedef {{start:number,end:number,length:number,substr:string}} TSequencePart\n */\n/** @type {TCodePoints} */\n\nconst code_points = [[0, 65535]];\nconst accent_pat = '[\\u0300-\\u036F\\u{b7}\\u{2be}\\u{2bc}]';\n/** @type {TUnicodeMap} */\n\nlet unicode_map;\n/** @type {RegExp} */\n\nlet multi_char_reg;\nconst max_char_length = 3;\n/** @type {TUnicodeMap} */\n\nconst latin_convert = {};\n/** @type {TUnicodeMap} */\n\nconst latin_condensed = {\n '/': '⁄∕',\n '0': '߀',\n \"a\": \"ⱥɐɑ\",\n \"aa\": \"ꜳ\",\n \"ae\": \"æǽǣ\",\n \"ao\": \"ꜵ\",\n \"au\": \"ꜷ\",\n \"av\": \"ꜹꜻ\",\n \"ay\": \"ꜽ\",\n \"b\": \"ƀɓƃ\",\n \"c\": \"ꜿƈȼↄ\",\n \"d\": \"đɗɖᴅƌꮷԁɦ\",\n \"e\": \"ɛǝᴇɇ\",\n \"f\": \"ꝼƒ\",\n \"g\": \"ǥɠꞡᵹꝿɢ\",\n \"h\": \"ħⱨⱶɥ\",\n \"i\": \"ɨı\",\n \"j\": \"ɉȷ\",\n \"k\": \"ƙⱪꝁꝃꝅꞣ\",\n \"l\": \"łƚɫⱡꝉꝇꞁɭ\",\n \"m\": \"ɱɯϻ\",\n \"n\": \"ꞥƞɲꞑᴎлԉ\",\n \"o\": \"øǿɔɵꝋꝍᴑ\",\n \"oe\": \"œ\",\n \"oi\": \"ƣ\",\n \"oo\": \"ꝏ\",\n \"ou\": \"ȣ\",\n \"p\": \"ƥᵽꝑꝓꝕρ\",\n \"q\": \"ꝗꝙɋ\",\n \"r\": \"ɍɽꝛꞧꞃ\",\n \"s\": \"ßȿꞩꞅʂ\",\n \"t\": \"ŧƭʈⱦꞇ\",\n \"th\": \"þ\",\n \"tz\": \"ꜩ\",\n \"u\": \"ʉ\",\n \"v\": \"ʋꝟʌ\",\n \"vy\": \"ꝡ\",\n \"w\": \"ⱳ\",\n \"y\": \"ƴɏỿ\",\n \"z\": \"ƶȥɀⱬꝣ\",\n \"hv\": \"ƕ\"\n};\n\nfor (let latin in latin_condensed) {\n let unicode = latin_condensed[latin] || '';\n\n for (let i = 0; i < unicode.length; i++) {\n let char = unicode.substring(i, i + 1);\n latin_convert[char] = latin;\n }\n}\n\nconst convert_pat = new RegExp(Object.keys(latin_convert).join('|') + '|' + accent_pat, 'gu');\n/**\n * Initialize the unicode_map from the give code point ranges\n *\n * @param {TCodePoints=} _code_points\n */\n\nconst initialize = _code_points => {\n if (unicode_map !== undefined) return;\n unicode_map = generateMap(_code_points || code_points);\n};\n/**\n * Helper method for normalize a string\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize\n * @param {string} str\n * @param {string} form\n */\n\nconst normalize = (str, form = 'NFKD') => str.normalize(form);\n/**\n * Remove accents without reordering string\n * calling str.normalize('NFKD') on \\u{594}\\u{595}\\u{596} becomes \\u{596}\\u{594}\\u{595}\n * via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703\n * @param {string} str\n * @return {string}\n */\n\nconst asciifold = str => {\n return toArray(str).reduce(\n /**\n * @param {string} result\n * @param {string} char\n */\n (result, char) => {\n return result + _asciifold(char);\n }, '');\n};\n/**\n * @param {string} str\n * @return {string}\n */\n\nconst _asciifold = str => {\n str = normalize(str).toLowerCase().replace(convert_pat, (\n /** @type {string} */\n char) => {\n return latin_convert[char] || '';\n }); //return str;\n\n return normalize(str, 'NFC');\n};\n/**\n * Generate a list of unicode variants from the list of code points\n * @param {TCodePoints} code_points\n * @yield {TCodePointObj}\n */\n\nfunction* generator(code_points) {\n for (const [code_point_min, code_point_max] of code_points) {\n for (let i = code_point_min; i <= code_point_max; i++) {\n let composed = String.fromCharCode(i);\n let folded = asciifold(composed);\n\n if (folded == composed.toLowerCase()) {\n continue;\n } // skip when folded is a string longer than 3 characters long\n // bc the resulting regex patterns will be long\n // eg:\n // folded صلى الله عليه وسلم length 18 code point 65018\n // folded جل جلاله length 8 code point 65019\n\n\n if (folded.length > max_char_length) {\n continue;\n }\n\n if (folded.length == 0) {\n continue;\n }\n\n yield {\n folded: folded,\n composed: composed,\n code_point: i\n };\n }\n }\n}\n/**\n * Generate a unicode map from the list of code points\n * @param {TCodePoints} code_points\n * @return {TUnicodeSets}\n */\n\nconst generateSets = code_points => {\n /** @type {{[key:string]:Set}} */\n const unicode_sets = {};\n /**\n * @param {string} folded\n * @param {string} to_add\n */\n\n const addMatching = (folded, to_add) => {\n /** @type {Set} */\n const folded_set = unicode_sets[folded] || new Set();\n const patt = new RegExp('^' + setToPattern(folded_set) + '$', 'iu');\n\n if (to_add.match(patt)) {\n return;\n }\n\n folded_set.add(escape_regex(to_add));\n unicode_sets[folded] = folded_set;\n };\n\n for (let value of generator(code_points)) {\n addMatching(value.folded, value.folded);\n addMatching(value.folded, value.composed);\n }\n\n return unicode_sets;\n};\n/**\n * Generate a unicode map from the list of code points\n * ae => (?:(?:ae|Æ|Ǽ|Ǣ)|(?:A|Ⓐ|A...)(?:E|ɛ|Ⓔ...))\n *\n * @param {TCodePoints} code_points\n * @return {TUnicodeMap}\n */\n\nconst generateMap = code_points => {\n /** @type {TUnicodeSets} */\n const unicode_sets = generateSets(code_points);\n /** @type {TUnicodeMap} */\n\n const unicode_map = {};\n /** @type {string[]} */\n\n let multi_char = [];\n\n for (let folded in unicode_sets) {\n let set = unicode_sets[folded];\n\n if (set) {\n unicode_map[folded] = setToPattern(set);\n }\n\n if (folded.length > 1) {\n multi_char.push(escape_regex(folded));\n }\n }\n\n multi_char.sort((a, b) => b.length - a.length);\n const multi_char_patt = arrayToPattern(multi_char);\n multi_char_reg = new RegExp('^' + multi_char_patt, 'u');\n return unicode_map;\n};\n/**\n * Map each element of an array from it's folded value to all possible unicode matches\n * @param {string[]} strings\n * @param {number} min_replacement\n * @return {string}\n */\n\nconst mapSequence = (strings, min_replacement = 1) => {\n let chars_replaced = 0;\n strings = strings.map(str => {\n if (unicode_map[str]) {\n chars_replaced += str.length;\n }\n\n return unicode_map[str] || str;\n });\n\n if (chars_replaced >= min_replacement) {\n return sequencePattern(strings);\n }\n\n return '';\n};\n/**\n * Convert a short string and split it into all possible patterns\n * Keep a pattern only if min_replacement is met\n *\n * 'abc'\n * \t\t=> [['abc'],['ab','c'],['a','bc'],['a','b','c']]\n *\t\t=> ['abc-pattern','ab-c-pattern'...]\n *\n *\n * @param {string} str\n * @param {number} min_replacement\n * @return {string}\n */\n\nconst substringsToPattern = (str, min_replacement = 1) => {\n min_replacement = Math.max(min_replacement, str.length - 1);\n return arrayToPattern(allSubstrings(str).map(sub_pat => {\n return mapSequence(sub_pat, min_replacement);\n }));\n};\n/**\n * Convert an array of sequences into a pattern\n * [{start:0,end:3,length:3,substr:'iii'}...] => (?:iii...)\n *\n * @param {Sequence[]} sequences\n * @param {boolean} all\n */\n\nconst sequencesToPattern = (sequences, all = true) => {\n let min_replacement = sequences.length > 1 ? 1 : 0;\n return arrayToPattern(sequences.map(sequence => {\n let seq = [];\n const len = all ? sequence.length() : sequence.length() - 1;\n\n for (let j = 0; j < len; j++) {\n seq.push(substringsToPattern(sequence.substrs[j] || '', min_replacement));\n }\n\n return sequencePattern(seq);\n }));\n};\n/**\n * Return true if the sequence is already in the sequences\n * @param {Sequence} needle_seq\n * @param {Sequence[]} sequences\n */\n\n\nconst inSequences = (needle_seq, sequences) => {\n for (const seq of sequences) {\n if (seq.start != needle_seq.start || seq.end != needle_seq.end) {\n continue;\n }\n\n if (seq.substrs.join('') !== needle_seq.substrs.join('')) {\n continue;\n }\n\n let needle_parts = needle_seq.parts;\n /**\n * @param {TSequencePart} part\n */\n\n const filter = part => {\n for (const needle_part of needle_parts) {\n if (needle_part.start === part.start && needle_part.substr === part.substr) {\n return false;\n }\n\n if (part.length == 1 || needle_part.length == 1) {\n continue;\n } // check for overlapping parts\n // a = ['::=','==']\n // b = ['::','===']\n // a = ['r','sm']\n // b = ['rs','m']\n\n\n if (part.start < needle_part.start && part.end > needle_part.start) {\n return true;\n }\n\n if (needle_part.start < part.start && needle_part.end > part.start) {\n return true;\n }\n }\n\n return false;\n };\n\n let filtered = seq.parts.filter(filter);\n\n if (filtered.length > 0) {\n continue;\n }\n\n return true;\n }\n\n return false;\n};\n\nclass Sequence {\n constructor() {\n /** @type {TSequencePart[]} */\n this.parts = [];\n /** @type {string[]} */\n\n this.substrs = [];\n this.start = 0;\n this.end = 0;\n }\n /**\n * @param {TSequencePart|undefined} part\n */\n\n\n add(part) {\n if (part) {\n this.parts.push(part);\n this.substrs.push(part.substr);\n this.start = Math.min(part.start, this.start);\n this.end = Math.max(part.end, this.end);\n }\n }\n\n last() {\n return this.parts[this.parts.length - 1];\n }\n\n length() {\n return this.parts.length;\n }\n /**\n * @param {number} position\n * @param {TSequencePart} last_piece\n */\n\n\n clone(position, last_piece) {\n let clone = new Sequence();\n let parts = JSON.parse(JSON.stringify(this.parts));\n let last_part = parts.pop();\n\n for (const part of parts) {\n clone.add(part);\n }\n\n let last_substr = last_piece.substr.substring(0, position - last_part.start);\n let clone_last_len = last_substr.length;\n clone.add({\n start: last_part.start,\n end: last_part.start + clone_last_len,\n length: clone_last_len,\n substr: last_substr\n });\n return clone;\n }\n\n}\n/**\n * Expand a regular expression pattern to include unicode variants\n * \teg /a/ becomes /aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐɑAⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ/\n *\n * Issue:\n * ﺊﺋ [ 'ﺊ = \\\\u{fe8a}', 'ﺋ = \\\\u{fe8b}' ]\n *\tbecomes:\tئئ [ 'ي = \\\\u{64a}', 'ٔ = \\\\u{654}', 'ي = \\\\u{64a}', 'ٔ = \\\\u{654}' ]\n *\n *\tİIJ = IIJ = ⅡJ\n *\n * \t1/2/4\n *\n * @param {string} str\n * @return {string|undefined}\n */\n\n\nconst getPattern = str => {\n initialize();\n str = asciifold(str);\n let pattern = '';\n let sequences = [new Sequence()];\n\n for (let i = 0; i < str.length; i++) {\n let substr = str.substring(i);\n let match = substr.match(multi_char_reg);\n const char = str.substring(i, i + 1);\n const match_str = match ? match[0] : null; // loop through sequences\n // add either the char or multi_match\n\n let overlapping = [];\n let added_types = new Set();\n\n for (const sequence of sequences) {\n const last_piece = sequence.last();\n\n if (!last_piece || last_piece.length == 1 || last_piece.end <= i) {\n // if we have a multi match\n if (match_str) {\n const len = match_str.length;\n sequence.add({\n start: i,\n end: i + len,\n length: len,\n substr: match_str\n });\n added_types.add('1');\n } else {\n sequence.add({\n start: i,\n end: i + 1,\n length: 1,\n substr: char\n });\n added_types.add('2');\n }\n } else if (match_str) {\n let clone = sequence.clone(i, last_piece);\n const len = match_str.length;\n clone.add({\n start: i,\n end: i + len,\n length: len,\n substr: match_str\n });\n overlapping.push(clone);\n } else {\n // don't add char\n // adding would create invalid patterns: 234 => [2,34,4]\n added_types.add('3');\n }\n } // if we have overlapping\n\n\n if (overlapping.length > 0) {\n // ['ii','iii'] before ['i','i','iii']\n overlapping = overlapping.sort((a, b) => {\n return a.length() - b.length();\n });\n\n for (let clone of overlapping) {\n // don't add if we already have an equivalent sequence\n if (inSequences(clone, sequences)) {\n continue;\n }\n\n sequences.push(clone);\n }\n\n continue;\n } // if we haven't done anything unique\n // clean up the patterns\n // helps keep patterns smaller\n // if str = 'r₨㎧aarss', pattern will be 446 instead of 655\n\n\n if (i > 0 && added_types.size == 1 && !added_types.has('3')) {\n pattern += sequencesToPattern(sequences, false);\n let new_seq = new Sequence();\n const old_seq = sequences[0];\n\n if (old_seq) {\n new_seq.add(old_seq.last());\n }\n\n sequences = [new_seq];\n }\n }\n\n pattern += sequencesToPattern(sequences, true);\n return pattern;\n};\n\nexport { _asciifold, asciifold, code_points, generateMap, generateSets, generator, getPattern, initialize, mapSequence, normalize, substringsToPattern, unicode_map };\n//# sourceMappingURL=index.js.map\n","/*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */\nimport { asciifold } from '@orchidjs/unicode-variants';\n\n/**\n * A property getter resolving dot-notation\n * @param {Object} obj The root object to fetch property on\n * @param {String} name The optionally dotted property name to fetch\n * @return {Object} The resolved property value\n */\nconst getAttr = (obj, name) => {\n if (!obj) return;\n return obj[name];\n};\n/**\n * A property getter resolving dot-notation\n * @param {Object} obj The root object to fetch property on\n * @param {String} name The optionally dotted property name to fetch\n * @return {Object} The resolved property value\n */\n\nconst getAttrNesting = (obj, name) => {\n if (!obj) return;\n var part,\n names = name.split(\".\");\n\n while ((part = names.shift()) && (obj = obj[part]));\n\n return obj;\n};\n/**\n * Calculates how close of a match the\n * given value is against a search token.\n *\n */\n\nconst scoreValue = (value, token, weight) => {\n var score, pos;\n if (!value) return 0;\n value = value + '';\n if (token.regex == null) return 0;\n pos = value.search(token.regex);\n if (pos === -1) return 0;\n score = token.string.length / value.length;\n if (pos === 0) score += 0.5;\n return score * weight;\n};\n/**\n * Cast object property to an array if it exists and has a value\n *\n */\n\nconst propToArray = (obj, key) => {\n var value = obj[key];\n if (typeof value == 'function') return value;\n\n if (value && !Array.isArray(value)) {\n obj[key] = [value];\n }\n};\n/**\n * Iterates over arrays and hashes.\n *\n * ```\n * iterate(this.items, function(item, id) {\n * // invoked for each item\n * });\n * ```\n *\n */\n\nconst iterate = (object, callback) => {\n if (Array.isArray(object)) {\n object.forEach(callback);\n } else {\n for (var key in object) {\n if (object.hasOwnProperty(key)) {\n callback(object[key], key);\n }\n }\n }\n};\nconst cmp = (a, b) => {\n if (typeof a === 'number' && typeof b === 'number') {\n return a > b ? 1 : a < b ? -1 : 0;\n }\n\n a = asciifold(a + '').toLowerCase();\n b = asciifold(b + '').toLowerCase();\n if (a > b) return 1;\n if (b > a) return -1;\n return 0;\n};\n\nexport { cmp, getAttr, getAttrNesting, iterate, propToArray, scoreValue };\n//# sourceMappingURL=utils.js.map\n","/*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */\nimport { iterate, cmp, propToArray, getAttrNesting, getAttr, scoreValue } from './utils.js';\nexport { cmp, getAttr, getAttrNesting, iterate, propToArray, scoreValue } from './utils.js';\nimport { escape_regex, getPattern } from '@orchidjs/unicode-variants';\nexport { getPattern } from '@orchidjs/unicode-variants';\n\n/**\n * sifter.js\n * Copyright (c) 2013–2020 Brian Reavis & contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this\n * file except in compliance with the License. You may obtain a copy of the License at:\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n * ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n *\n * @author Brian Reavis \n */\n\nclass Sifter {\n // []|{};\n\n /**\n * Textually searches arrays and hashes of objects\n * by property (or multiple properties). Designed\n * specifically for autocomplete.\n *\n */\n constructor(items, settings) {\n this.items = void 0;\n this.settings = void 0;\n this.items = items;\n this.settings = settings || {\n diacritics: true\n };\n }\n\n /**\n * Splits a search string into an array of individual\n * regexps to be used to match results.\n *\n */\n tokenize(query, respect_word_boundaries, weights) {\n if (!query || !query.length) return [];\n const tokens = [];\n const words = query.split(/\\s+/);\n var field_regex;\n\n if (weights) {\n field_regex = new RegExp('^(' + Object.keys(weights).map(escape_regex).join('|') + ')\\:(.*)$');\n }\n\n words.forEach(word => {\n let field_match;\n let field = null;\n let regex = null; // look for \"field:query\" tokens\n\n if (field_regex && (field_match = word.match(field_regex))) {\n field = field_match[1];\n word = field_match[2];\n }\n\n if (word.length > 0) {\n if (this.settings.diacritics) {\n regex = getPattern(word) || null;\n } else {\n regex = escape_regex(word);\n }\n\n if (regex && respect_word_boundaries) regex = \"\\\\b\" + regex;\n }\n\n tokens.push({\n string: word,\n regex: regex ? new RegExp(regex, 'iu') : null,\n field: field\n });\n });\n return tokens;\n }\n\n /**\n * Returns a function to be used to score individual results.\n *\n * Good matches will have a higher score than poor matches.\n * If an item is not a match, 0 will be returned by the function.\n *\n * @returns {T.ScoreFn}\n */\n getScoreFunction(query, options) {\n var search = this.prepareSearch(query, options);\n return this._getScoreFunction(search);\n }\n /**\n * @returns {T.ScoreFn}\n *\n */\n\n\n _getScoreFunction(search) {\n const tokens = search.tokens,\n token_count = tokens.length;\n\n if (!token_count) {\n return function () {\n return 0;\n };\n }\n\n const fields = search.options.fields,\n weights = search.weights,\n field_count = fields.length,\n getAttrFn = search.getAttrFn;\n\n if (!field_count) {\n return function () {\n return 1;\n };\n }\n /**\n * Calculates the score of an object\n * against the search query.\n *\n */\n\n\n const scoreObject = function () {\n if (field_count === 1) {\n return function (token, data) {\n const field = fields[0].field;\n return scoreValue(getAttrFn(data, field), token, weights[field] || 1);\n };\n }\n\n return function (token, data) {\n var sum = 0; // is the token specific to a field?\n\n if (token.field) {\n const value = getAttrFn(data, token.field);\n\n if (!token.regex && value) {\n sum += 1 / field_count;\n } else {\n sum += scoreValue(value, token, 1);\n }\n } else {\n iterate(weights, (weight, field) => {\n sum += scoreValue(getAttrFn(data, field), token, weight);\n });\n }\n\n return sum / field_count;\n };\n }();\n\n if (token_count === 1) {\n return function (data) {\n return scoreObject(tokens[0], data);\n };\n }\n\n if (search.options.conjunction === 'and') {\n return function (data) {\n var score,\n sum = 0;\n\n for (let token of tokens) {\n score = scoreObject(token, data);\n if (score <= 0) return 0;\n sum += score;\n }\n\n return sum / token_count;\n };\n } else {\n return function (data) {\n var sum = 0;\n iterate(tokens, token => {\n sum += scoreObject(token, data);\n });\n return sum / token_count;\n };\n }\n }\n\n /**\n * Returns a function that can be used to compare two\n * results, for sorting purposes. If no sorting should\n * be performed, `null` will be returned.\n *\n * @return function(a,b)\n */\n getSortFunction(query, options) {\n var search = this.prepareSearch(query, options);\n return this._getSortFunction(search);\n }\n\n _getSortFunction(search) {\n var implicit_score,\n sort_flds = [];\n const self = this,\n options = search.options,\n sort = !search.query && options.sort_empty ? options.sort_empty : options.sort;\n\n if (typeof sort == 'function') {\n return sort.bind(this);\n }\n /**\n * Fetches the specified sort field value\n * from a search result item.\n *\n */\n\n\n const get_field = function get_field(name, result) {\n if (name === '$score') return result.score;\n return search.getAttrFn(self.items[result.id], name);\n }; // parse options\n\n\n if (sort) {\n for (let s of sort) {\n if (search.query || s.field !== '$score') {\n sort_flds.push(s);\n }\n }\n } // the \"$score\" field is implied to be the primary\n // sort field, unless it's manually specified\n\n\n if (search.query) {\n implicit_score = true;\n\n for (let fld of sort_flds) {\n if (fld.field === '$score') {\n implicit_score = false;\n break;\n }\n }\n\n if (implicit_score) {\n sort_flds.unshift({\n field: '$score',\n direction: 'desc'\n });\n } // without a search.query, all items will have the same score\n\n } else {\n sort_flds = sort_flds.filter(fld => fld.field !== '$score');\n } // build function\n\n\n const sort_flds_count = sort_flds.length;\n\n if (!sort_flds_count) {\n return null;\n }\n\n return function (a, b) {\n var result, field;\n\n for (let sort_fld of sort_flds) {\n field = sort_fld.field;\n let multiplier = sort_fld.direction === 'desc' ? -1 : 1;\n result = multiplier * cmp(get_field(field, a), get_field(field, b));\n if (result) return result;\n }\n\n return 0;\n };\n }\n\n /**\n * Parses a search query and returns an object\n * with tokens and fields ready to be populated\n * with results.\n *\n */\n prepareSearch(query, optsUser) {\n const weights = {};\n var options = Object.assign({}, optsUser);\n propToArray(options, 'sort');\n propToArray(options, 'sort_empty'); // convert fields to new format\n\n if (options.fields) {\n propToArray(options, 'fields');\n const fields = [];\n options.fields.forEach(field => {\n if (typeof field == 'string') {\n field = {\n field: field,\n weight: 1\n };\n }\n\n fields.push(field);\n weights[field.field] = 'weight' in field ? field.weight : 1;\n });\n options.fields = fields;\n }\n\n return {\n options: options,\n query: query.toLowerCase().trim(),\n tokens: this.tokenize(query, options.respect_word_boundaries, weights),\n total: 0,\n items: [],\n weights: weights,\n getAttrFn: options.nesting ? getAttrNesting : getAttr\n };\n }\n\n /**\n * Searches through all items and returns a sorted array of matches.\n *\n */\n search(query, options) {\n var self = this,\n score,\n search;\n search = this.prepareSearch(query, options);\n options = search.options;\n query = search.query; // generate result scoring function\n\n const fn_score = options.score || self._getScoreFunction(search); // perform search and sort\n\n\n if (query.length) {\n iterate(self.items, (item, id) => {\n score = fn_score(item);\n\n if (options.filter === false || score > 0) {\n search.items.push({\n 'score': score,\n 'id': id\n });\n }\n });\n } else {\n iterate(self.items, (_, id) => {\n search.items.push({\n 'score': 1,\n 'id': id\n });\n });\n }\n\n const fn_sort = self._getSortFunction(search);\n\n if (fn_sort) search.items.sort(fn_sort); // apply limits\n\n search.total = search.items.length;\n\n if (typeof options.limit === 'number') {\n search.items = search.items.slice(0, options.limit);\n }\n\n return search;\n }\n\n}\n\nexport { Sifter };\n//# sourceMappingURL=sifter.js.map\n","\nimport { asciifold } from '@orchidjs/unicode-variants';\nimport * as T from './types';\n\n\n/**\n * A property getter resolving dot-notation\n * @param {Object} obj The root object to fetch property on\n * @param {String} name The optionally dotted property name to fetch\n * @return {Object} The resolved property value\n */\nexport const getAttr = (obj:{[key:string]:any}, name:string ) => {\n if (!obj ) return;\n return obj[name];\n};\n\n/**\n * A property getter resolving dot-notation\n * @param {Object} obj The root object to fetch property on\n * @param {String} name The optionally dotted property name to fetch\n * @return {Object} The resolved property value\n */\nexport const getAttrNesting = (obj:{[key:string]:any}, name:string ) => {\n if (!obj ) return;\n var part, names = name.split(\".\");\n\twhile( (part = names.shift()) && (obj = obj[part]));\n return obj;\n};\n\n/**\n * Calculates how close of a match the\n * given value is against a search token.\n *\n */\nexport const scoreValue = (value:string, token:T.Token, weight:number ):number => {\n\tvar score, pos;\n\n\tif (!value) return 0;\n\n\tvalue = value + '';\n\tif( token.regex == null ) return 0;\n\tpos = value.search(token.regex);\n\tif (pos === -1) return 0;\n\n\tscore = token.string.length / value.length;\n\tif (pos === 0) score += 0.5;\n\n\treturn score * weight;\n};\n\n\n/**\n * Cast object property to an array if it exists and has a value\n *\n */\nexport const propToArray = (obj:{[key:string]:any}, key:string) => {\n\tvar value = obj[key];\n\n\tif( typeof value == 'function' ) return value;\n\n\tif( value && !Array.isArray(value) ){\n\t\tobj[key] = [value];\n\t}\n}\n\n\n/**\n * Iterates over arrays and hashes.\n *\n * ```\n * iterate(this.items, function(item, id) {\n * // invoked for each item\n * });\n * ```\n *\n */\nexport const iterate = (object:[]|{[key:string]:any}, callback:(value:any,key:any)=>any) => {\n\n\tif ( Array.isArray(object)) {\n\t\tobject.forEach(callback);\n\n\t}else{\n\n\t\tfor (var key in object) {\n\t\t\tif (object.hasOwnProperty(key)) {\n\t\t\t\tcallback(object[key], key);\n\t\t\t}\n\t\t}\n\t}\n};\n\n\n\nexport const cmp = (a:number|string, b:number|string) => {\n\tif (typeof a === 'number' && typeof b === 'number') {\n\t\treturn a > b ? 1 : (a < b ? -1 : 0);\n\t}\n\ta = asciifold(a + '').toLowerCase();\n\tb = asciifold(b + '').toLowerCase();\n\tif (a > b) return 1;\n\tif (b > a) return -1;\n\treturn 0;\n};\n","\nimport { iterate } from '@orchidjs/sifter/lib/utils';\n\n/**\n * Return a dom element from either a dom query string, jQuery object, a dom element or html string\n * https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518\n *\n * param query should be {}\n */\nexport const getDom = ( query:any ):HTMLElement => {\n\n\tif( query.jquery ){\n\t\treturn query[0];\n\t}\n\n\tif( query instanceof HTMLElement ){\n\t\treturn query;\n\t}\n\n\tif( isHtmlString(query) ){\n\t\tvar tpl = document.createElement('template');\n\t\ttpl.innerHTML = query.trim(); // Never return a text node of whitespace as the result\n\t\treturn tpl.content.firstChild as HTMLElement;\n\t}\n\n\treturn document.querySelector(query);\n};\n\nexport const isHtmlString = (arg:any): boolean => {\n\tif( typeof arg === 'string' && arg.indexOf('<') > -1 ){\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nexport const escapeQuery = (query:string):string => {\n\treturn query.replace(/['\"\\\\]/g, '\\\\$&');\n}\n\n/**\n * Dispatch an event\n *\n */\nexport const triggerEvent = ( dom_el:HTMLElement, event_name:string ):void => {\n\tvar event = document.createEvent('HTMLEvents');\n\tevent.initEvent(event_name, true, false);\n\tdom_el.dispatchEvent(event)\n};\n\n/**\n * Apply CSS rules to a dom element\n *\n */\nexport const applyCSS = ( dom_el:HTMLElement, css:{ [key: string]: string|number }):void => {\n\tObject.assign(dom_el.style, css);\n}\n\n\n/**\n * Add css classes\n *\n */\nexport const addClasses = ( elmts:HTMLElement|HTMLElement[], ...classes:string[]|string[][] ) => {\n\n\tvar norm_classes \t= classesArray(classes);\n\telmts\t\t\t\t= castAsArray(elmts);\n\n\telmts.map( el => {\n\t\tnorm_classes.map( cls => {\n\t\t\tel.classList.add( cls );\n\t\t});\n\t});\n}\n\n/**\n * Remove css classes\n *\n */\n export const removeClasses = ( elmts:HTMLElement|HTMLElement[], ...classes:string[]|string[][] ) => {\n\n \tvar norm_classes \t= classesArray(classes);\n\telmts\t\t\t\t= castAsArray(elmts);\n\n\telmts.map( el => {\n\t\tnorm_classes.map(cls => {\n\t \t\tel.classList.remove( cls );\n\t\t});\n \t});\n }\n\n\n/**\n * Return arguments\n *\n */\nexport const classesArray = (args:string[]|string[][]):string[] => {\n\tvar classes:string[] = [];\n\titerate( args, (_classes) =>{\n\t\tif( typeof _classes === 'string' ){\n\t\t\t_classes = _classes.trim().split(/[\\11\\12\\14\\15\\40]/);\n\t\t}\n\t\tif( Array.isArray(_classes) ){\n\t\t\tclasses = classes.concat(_classes);\n\t\t}\n\t});\n\n\treturn classes.filter(Boolean);\n}\n\n\n/**\n * Create an array from arg if it's not already an array\n *\n */\nexport const castAsArray = (arg:any):Array => {\n\tif( !Array.isArray(arg) ){\n \t\targ = [arg];\n \t}\n\treturn arg;\n}\n\n\n/**\n * Get the closest node to the evt.target matching the selector\n * Stops at wrapper\n *\n */\nexport const parentMatch = ( target:null|HTMLElement, selector:string, wrapper?:HTMLElement ):HTMLElement|void => {\n\n\tif( wrapper && !wrapper.contains(target) ){\n\t\treturn;\n\t}\n\n\twhile( target && target.matches ){\n\n\t\tif( target.matches(selector) ){\n\t\t\treturn target;\n\t\t}\n\n\t\ttarget = target.parentNode as HTMLElement;\n\t}\n}\n\n\n/**\n * Get the first or last item from an array\n *\n * > 0 - right (last)\n * <= 0 - left (first)\n *\n */\nexport const getTail = ( list:Array|NodeList, direction:number=0 ):any => {\n\n\tif( direction > 0 ){\n\t\treturn list[list.length-1];\n\t}\n\n\treturn list[0];\n}\n\n/**\n * Return true if an object is empty\n *\n */\nexport const isEmptyObject = (obj:object):boolean => {\n\treturn (Object.keys(obj).length === 0);\n}\n\n\n/**\n * Get the index of an element amongst sibling nodes of the same type\n *\n */\nexport const nodeIndex = ( el:null|Element, amongst?:string ):number => {\n\tif (!el) return -1;\n\n\tamongst = amongst || el.nodeName;\n\n\tvar i = 0;\n\twhile( el = el.previousElementSibling ){\n\n\t\tif( el.matches(amongst) ){\n\t\t\ti++;\n\t\t}\n\t}\n\treturn i;\n}\n\n\n/**\n * Set attributes of an element\n *\n */\nexport const setAttr = (el:Element,attrs:{ [key: string]: null|string|number }) => {\n\titerate( attrs,(val,attr) => {\n\t\tif( val == null ){\n\t\t\tel.removeAttribute(attr as string);\n\t\t}else{\n\t\t\tel.setAttribute(attr as string, ''+val);\n\t\t}\n\t});\n}\n\n\n/**\n * Replace a node\n */\nexport const replaceNode = ( existing:Node, replacement:Node ) => {\n\tif( existing.parentNode ) existing.parentNode.replaceChild(replacement, existing);\n}\n","/**\n * highlight v3 | MIT license | Johann Burkard \n * Highlights arbitrary terms in a node.\n *\n * - Modified by Marshal 2011-6-24 (added regex)\n * - Modified by Brian Reavis 2012-8-27 (cleanup)\n */\n\nimport {replaceNode} from '../vanilla';\n\n\nexport const highlight = (element:HTMLElement, regex:string|RegExp) => {\n\n\tif( regex === null ) return;\n\n\t// convet string to regex\n\tif( typeof regex === 'string' ){\n\n\t\tif( !regex.length ) return;\n\t\tregex = new RegExp(regex, 'i');\n\t}\n\n\n\t// Wrap matching part of text node with highlighting , e.g.\n\t// Soccer -> Soccer for regex = /soc/i\n\tconst highlightText = ( node:Text ):number => {\n\n\t\tvar match = node.data.match(regex);\n\t\tif( match && node.data.length > 0 ){\n\t\t\tvar spannode\t\t= document.createElement('span');\n\t\t\tspannode.className\t= 'highlight';\n\t\t\tvar middlebit\t\t= node.splitText(match.index as number);\n\n\t\t\tmiddlebit.splitText(match[0]!.length);\n\t\t\tvar middleclone\t\t= middlebit.cloneNode(true);\n\n\t\t\tspannode.appendChild(middleclone);\n\t\t\treplaceNode(middlebit, spannode);\n\t\t\treturn 1;\n\t\t}\n\n\t\treturn 0;\n\t};\n\n\t// Recurse element node, looking for child text nodes to highlight, unless element\n\t// is childless,