diff --git a/contrib/openapi.json b/contrib/openapi.json index 09a25acd3..0c7d71ab0 100644 --- a/contrib/openapi.json +++ b/contrib/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "NetBox REST API", - "version": "4.6.7", + "version": "4.6.8", "license": { "name": "Apache v2 License" } @@ -150938,33 +150938,6 @@ "description": "" } } - }, - "post": { - "operationId": "extras_scripts_create", - "description": "Post a list of script objects.", - "tags": [ - "extras" - ], - "security": [ - { - "cookieAuth": [] - }, - { - "tokenAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Script" - } - } - }, - "description": "" - } - } } }, "/api/extras/scripts/{id}/": { @@ -151030,9 +151003,9 @@ } } }, - "put": { - "operationId": "extras_scripts_update", - "description": "Put a script object.", + "post": { + "operationId": "extras_scripts_run", + "description": "Run a Script identified by its numeric PK or module & name and return the pending Job as the result", "parameters": [ { "in": "path", @@ -151075,94 +151048,11 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Script" + "$ref": "#/components/schemas/ScriptDetail" } } }, - "description": "" - } - } - }, - "patch": { - "operationId": "extras_scripts_partial_update", - "description": "Patch a script object.", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "string", - "pattern": "^[^/]+$" - }, - "required": true - } - ], - "tags": [ - "extras" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedScriptInputRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedScriptInputRequest" - } - } - } - }, - "security": [ - { - "cookieAuth": [] - }, - { - "tokenAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Script" - } - } - }, - "description": "" - } - } - }, - "delete": { - "operationId": "extras_scripts_destroy", - "description": "Delete a script object.", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "string", - "pattern": "^[^/]+$" - }, - "required": true - } - ], - "tags": [ - "extras" - ], - "security": [ - { - "cookieAuth": [] - }, - { - "tokenAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" + "description": "The script has been enqueued for execution." } } } @@ -244416,16 +244306,6 @@ "user" ] }, - "BriefJobRequest": { - "type": "object", - "properties": { - "completed": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - }, "BriefL2VPN": { "type": "object", "description": "Base serializer class for models inheriting from PrimaryModel.", @@ -249575,11 +249455,13 @@ "enum": [ "IATA", "ISO_3166", - "UN_LOCODE" + "UN_LOCODE", + null ], "type": "string", "description": "* `IATA` - IATA (Airport codes)\n* `ISO_3166` - ISO 3166 (Country codes)\n* `UN_LOCODE` - UN/LOCODE (Location codes)", - "x-spec-enum-id": "cf0efb5195f85007" + "x-spec-enum-id": "cf0efb5195f85007", + "nullable": true }, "extra_choices": { "type": "array", @@ -260331,7 +260213,7 @@ "type": "string", "minLength": 1, "title": "URL", - "description": "This URL will be called using the HTTP method defined when the webhook is called. Jinja2 template processing is supported with the same context as the request body.", + "description": "This URL will be called using the HTTP method defined when the webhook is called. Must be http:// or https://. Jinja2 template processing is supported (with the same context as the request body) for part or all of the URL.", "maxLength": 500 }, "http_method": { @@ -266122,7 +266004,8 @@ "enum": [ "IATA", "ISO_3166", - "UN_LOCODE" + "UN_LOCODE", + null ], "type": "string", "description": "* `IATA` - IATA (Airport codes)\n* `ISO_3166` - ISO 3166 (Country codes)\n* `UN_LOCODE` - UN/LOCODE (Location codes)", @@ -266136,7 +266019,8 @@ "UN/LOCODE (Location codes)" ] } - } + }, + "nullable": true }, "extra_choices": { "type": "array", @@ -266228,11 +266112,13 @@ "enum": [ "IATA", "ISO_3166", - "UN_LOCODE" + "UN_LOCODE", + null ], "type": "string", "description": "* `IATA` - IATA (Airport codes)\n* `ISO_3166` - ISO 3166 (Country codes)\n* `UN_LOCODE` - UN/LOCODE (Location codes)", - "x-spec-enum-id": "cf0efb5195f85007" + "x-spec-enum-id": "cf0efb5195f85007", + "nullable": true }, "extra_choices": { "type": "array", @@ -287323,11 +287209,13 @@ "enum": [ "IATA", "ISO_3166", - "UN_LOCODE" + "UN_LOCODE", + null ], "type": "string", "description": "* `IATA` - IATA (Airport codes)\n* `ISO_3166` - ISO 3166 (Country codes)\n* `UN_LOCODE` - UN/LOCODE (Location codes)", - "x-spec-enum-id": "cf0efb5195f85007" + "x-spec-enum-id": "cf0efb5195f85007", + "nullable": true }, "extra_choices": { "type": "array", @@ -297860,7 +297748,7 @@ "type": "string", "minLength": 1, "title": "URL", - "description": "This URL will be called using the HTTP method defined when the webhook is called. Jinja2 template processing is supported with the same context as the request body.", + "description": "This URL will be called using the HTTP method defined when the webhook is called. Must be http:// or https://. Jinja2 template processing is supported (with the same context as the request body) for part or all of the URL.", "maxLength": 500 }, "http_method": { @@ -300843,35 +300731,6 @@ } } }, - "PatchedScriptInputRequest": { - "type": "object", - "properties": { - "data": {}, - "commit": { - "type": "boolean" - }, - "schedule_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "interval": { - "type": "integer", - "nullable": true - }, - "notifications": { - "enum": [ - "always", - "on_failure", - "never" - ], - "type": "string", - "description": "* `always` - Always\n* `on_failure` - On failure\n* `never` - Never", - "x-spec-enum-id": "57071f4400340a5c", - "default": "always" - } - } - }, "PatchedScriptModuleRequest": { "type": "object", "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", @@ -301681,7 +301540,7 @@ "type": "string", "minLength": 1, "title": "URL", - "description": "This URL will be called using the HTTP method defined when the webhook is called. Jinja2 template processing is supported with the same context as the request body.", + "description": "This URL will be called using the HTTP method defined when the webhook is called. Must be http:// or https://. Jinja2 template processing is supported (with the same context as the request body) for part or all of the URL.", "maxLength": 500 }, "http_method": { @@ -318061,6 +317920,71 @@ "vars" ] }, + "ScriptDetail": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display_url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "module": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "readOnly": true + }, + "description": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "vars": { + "nullable": true, + "readOnly": true + }, + "result": { + "allOf": [ + { + "$ref": "#/components/schemas/Job" + } + ], + "readOnly": true + }, + "display": { + "type": "string", + "readOnly": true + }, + "is_executable": { + "type": "boolean", + "readOnly": true + } + }, + "required": [ + "description", + "display", + "display_url", + "id", + "is_executable", + "module", + "name", + "result", + "url", + "vars" + ] + }, "ScriptInputRequest": { "type": "object", "properties": { @@ -324076,7 +324000,7 @@ "payload_url": { "type": "string", "title": "URL", - "description": "This URL will be called using the HTTP method defined when the webhook is called. Jinja2 template processing is supported with the same context as the request body.", + "description": "This URL will be called using the HTTP method defined when the webhook is called. Must be http:// or https://. Jinja2 template processing is supported (with the same context as the request body) for part or all of the URL.", "maxLength": 500 }, "http_method": { @@ -324185,7 +324109,7 @@ "type": "string", "minLength": 1, "title": "URL", - "description": "This URL will be called using the HTTP method defined when the webhook is called. Jinja2 template processing is supported with the same context as the request body.", + "description": "This URL will be called using the HTTP method defined when the webhook is called. Must be http:// or https://. Jinja2 template processing is supported (with the same context as the request body) for part or all of the URL.", "maxLength": 500 }, "http_method": { diff --git a/docs/release-notes/version-4.6.md b/docs/release-notes/version-4.6.md index 6d7db1575..77124ba0e 100644 --- a/docs/release-notes/version-4.6.md +++ b/docs/release-notes/version-4.6.md @@ -1,5 +1,32 @@ # NetBox v4.6 +## v4.6.8 (2026-08-11) + +### Performance Improvements + +* [#22787](https://github.com/netbox-community/netbox/issues/22787) - Avoid N+1 queries when resolving generic relations (e.g. assigned objects) via the GraphQL API +* [#22835](https://github.com/netbox-community/netbox/issues/22835) - Improve performance when provisioning new custom fields +* [#22837](https://github.com/netbox-community/netbox/issues/22837) - Omit implicit pagination when prefetching to-one relations via the GraphQL API +* [#22877](https://github.com/netbox-community/netbox/issues/22877) - Improve caching logic when retrieving custom fields via `get_for_model()` + +### Bug Fixes + +* [#22694](https://github.com/netbox-community/netbox/issues/22694) - Clear a device's stale rack assignment when changing its site +* [#22745](https://github.com/netbox-community/netbox/issues/22745) - Enforce object permissions on custom script write operations via the REST API +* [#22805](https://github.com/netbox-community/netbox/issues/22805) - Avoid re-executing the LDAP configuration file on every permission check +* [#22821](https://github.com/netbox-community/netbox/issues/22821) - Prevent the deletion of a tenant group from creating duplicate tenant names or slugs +* [#22825](https://github.com/netbox-community/netbox/issues/22825) - Fix cable path tracing for paths which originate from a circuit termination and traverse only pass-through ports +* [#22828](https://github.com/netbox-community/netbox/issues/22828) - Validate that a webhook's payload URL is a valid URL or Jinja2 template when saving +* [#22844](https://github.com/netbox-community/netbox/issues/22844) - Allow a null value for `base_choices` when creating a custom field choice set via the REST API +* [#22848](https://github.com/netbox-community/netbox/issues/22848) - Ensure deterministic ordering of duplicate IP addresses to avoid repeating an object across paginated REST API results +* [#22852](https://github.com/netbox-community/netbox/issues/22852) - Honor a custom script's `notifications_default` setting when the script is run from an event rule +* [#22865](https://github.com/netbox-community/netbox/issues/22865) - Reference the appropriate component template types on the GraphQL type for inventory item templates +* [#22879](https://github.com/netbox-community/netbox/issues/22879) - Improve the contrast of unselected radio buttons and checkboxes in dark mode +* [#22882](https://github.com/netbox-community/netbox/issues/22882) - Fix support for the `DISTINCT` filter on nested GraphQL list fields +* [#22894](https://github.com/netbox-community/netbox/issues/22894) - Sanitize the error message rendered when an exception occurs in `CustomLinkColumn` + +--- + ## v4.6.7 (2026-07-30) ### Performance Improvements diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 469257ae6..edb416782 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -1,7 +1,7 @@ from django import forms from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.forms.array import SimpleArrayField -from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist +from django.core.exceptions import NON_FIELD_ERRORS, MultipleObjectsReturned, ObjectDoesNotExist from django.utils.functional import lazy from django.utils.html import format_html from django.utils.safestring import SafeString, mark_safe @@ -1894,17 +1894,19 @@ class CableImportForm(PrimaryModelImportForm): **side_b_parent_params ) - def add_error(self, field, error): + def _update_errors(self, errors): # Remap any termination errors raised by Cable.clean() onto the relevant import column. - # Without this, Django raises ValueError for an error reported against a field which does - # not exist on the form. Errors are dispatched per key so that both sides can fall back to - # a non-field error without colliding. - if field is None and hasattr(error, 'error_dict'): - for name, errors in error.error_dict.items(): - super().add_error(self._map_termination_field(name), errors) - return + # Without this, the base form reports them as prefixed non-field errors, as neither + # a_terminations nor b_terminations exists as a field on this form. Errors are dispatched + # per key so that both sides can fall back to a non-field error without colliding. + if hasattr(errors, 'error_dict'): + remapped = {} + for name, error_list in errors.error_dict.items(): + mapped_name = self._map_termination_field(name) or NON_FIELD_ERRORS + remapped.setdefault(mapped_name, []).extend(error_list) + errors = forms.ValidationError(remapped) - super().add_error(self._map_termination_field(field), error) + super()._update_errors(errors) def _map_termination_field(self, field): """ diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index d0a9d9ef7..df715e5bb 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -3757,6 +3757,38 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase): instance.refresh_from_db() self.assertIsNone(instance.primary_mac_address) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_bulk_import_omitted_field_validation_error(self): + """Surface omitted-field validation errors during bulk updates.""" + device = Device.objects.first() + wireless_interface = Interface.objects.create( + device=device, + name='Wireless-22683', + type=InterfaceTypeChoices.TYPE_80211AC, + rf_channel_width=Decimal('20.0'), + ) + self.add_permissions('dcim.add_interface', 'dcim.change_interface') + csv_data = '\n'.join([ + 'id,type', + f'{wireless_interface.pk},{InterfaceTypeChoices.TYPE_1GE_GBIC}', + ]) + response = self.client.post( + self._get_url('bulk_import'), + data={ + 'data': csv_data, + 'format': ImportFormatChoices.CSV, + 'csv_delimiter': CSVDelimiterChoices.AUTO, + }, + ) + self.assertHttpStatus(response, 200) + self.assertContains( + response, + 'rf_channel_width: Channel width may be set only on wireless interfaces.', + ) + wireless_interface.refresh_from_db() + self.assertEqual(wireless_interface.type, InterfaceTypeChoices.TYPE_80211AC) + self.assertEqual(wireless_interface.rf_channel_width, Decimal('20.0')) + class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase): model = FrontPort diff --git a/netbox/extras/api/routers.py b/netbox/extras/api/routers.py new file mode 100644 index 000000000..e27797d7b --- /dev/null +++ b/netbox/extras/api/routers.py @@ -0,0 +1,34 @@ +from rest_framework.routers import Route + +from netbox.api.routers import NetBoxRouter + +from .views import ScriptViewSet + +__all__ = ( + 'ScriptRouter', +) + + +class ScriptRouter(NetBoxRouter): + """ + Extend NetBoxRouter to map POST on the script detail route to ScriptViewSet.run(). DRF's detail route + maps only the standard CRUD methods; absent this, run() must be declared as a raw post() method, which + binds to every route of the ViewSet and is invisible to per-action permissions & schema generation. + """ + def get_routes(self, viewset): + if not issubclass(viewset, ScriptViewSet): + return super().get_routes(viewset) + + # Extend the detail route template. Applied before super() expands the templates so that any + # @action routes are untouched; _replace() avoids mutating the templates shared by all routers. + routes = self.routes + self.routes = [ + route._replace(mapping={**route.mapping, 'post': 'run'}) + if isinstance(route, Route) and route.detail else route + for route in routes + ] + + try: + return super().get_routes(viewset) + finally: + self.routes = routes diff --git a/netbox/extras/api/urls.py b/netbox/extras/api/urls.py index cd1a9f683..dcc359c7a 100644 --- a/netbox/extras/api/urls.py +++ b/netbox/extras/api/urls.py @@ -1,10 +1,9 @@ from django.urls import include, path -from netbox.api.routers import NetBoxRouter - from . import views +from .routers import ScriptRouter -router = NetBoxRouter() +router = ScriptRouter() router.APIRootView = views.ExtrasRootView router.register('event-rules', views.EventRuleViewSet) diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 85dc5eca1..12827b938 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -1,16 +1,15 @@ from django.http import Http404 from django.shortcuts import get_object_or_404 from django.utils.translation import gettext_lazy as _ -from drf_spectacular.utils import OpenApiResponse, OpenApiTypes, extend_schema, extend_schema_view +from drf_spectacular.utils import OpenApiResponse, OpenApiTypes, extend_schema from rest_framework import status from rest_framework.decorators import action -from rest_framework.exceptions import PermissionDenied +from rest_framework.exceptions import PermissionDenied, ValidationError from rest_framework.generics import RetrieveUpdateDestroyAPIView from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.routers import APIRootView -from rest_framework.viewsets import ModelViewSet from core.choices import ManagedFileRootPathChoices from extras import filtersets @@ -308,30 +307,44 @@ class ScriptModuleViewSet(ObjectValidationMixin, CreateModelMixin, UpdateModelMi return obj -@extend_schema_view( - update=extend_schema(request=serializers.ScriptInputSerializer), - partial_update=extend_schema(request=serializers.ScriptInputSerializer), -) -class ScriptViewSet(ModelViewSet): +class ScriptViewSet(ListModelMixin, RetrieveModelMixin, BaseViewSet): + # Individual scripts are created, modified, and deleted through their module (see ScriptModuleViewSet), + # so the standard write actions are intentionally omitted here. Only listing/retrieving a script (GET) + # and running one (POST to the detail route) are supported. permission_classes = [IsAuthenticatedOrLoginNotRequired] queryset = Script.objects.all() serializer_class = serializers.ScriptSerializer filterset_class = filtersets.ScriptFilterSet - _ignore_model_permissions = True lookup_value_regex = '[^/]+' # Allow dots - def initial(self, request, *args, **kwargs): - super().initial(request, *args, **kwargs) + def get_serializer(self, *args, **kwargs): + # A POST to the detail route runs the script, taking ScriptInputSerializer as its request body. + # (This is keyed on the request method rather than on self.action, which is unset when generating + # OPTIONS metadata.) ScriptInputSerializer is instantiated directly rather than via BaseViewSet, + # which would pass it the fields/omit kwargs supported only by BaseModelSerializer. + if getattr(self.request, 'method', None) == 'POST': + kwargs.setdefault('context', self.get_serializer_context()) + return serializers.ScriptInputSerializer(*args, **kwargs) + return super().get_serializer(*args, **kwargs) - # Restrict the view's QuerySet to allow only the permitted objects - if request.user.is_authenticated: - action = 'run' if request.method == 'POST' else 'view' - self.queryset = self.queryset.restrict(request.user, action) + def get_serializer_context(self): + context = super().get_serializer_context() + + # ScriptInputSerializer resolves its field defaults and validates scheduling against the script + # being run (set by run() below). + context['script'] = getattr(self, 'script', None) + + return context def _get_script(self, pk): - # If pk is numeric, retrieve script by ID - if pk.isnumeric(): + # Retrieve the script by ID if the PK is all decimal digits. (isdecimal() rather than isnumeric(), + # as the latter also matches characters which cannot be cast to an integer.) + if pk.isdecimal(): + try: + pk = int(pk) + except ValueError: + raise Http404 return get_object_or_404(self.queryset, pk=pk) # Default to retrieval by module & name @@ -342,32 +355,49 @@ class ScriptViewSet(ModelViewSet): return get_object_or_404(self.queryset, module__file_path=f'{module_name}.py', name=script_name) - def retrieve(self, request, pk): + def retrieve(self, request, pk, **kwargs): script = self._get_script(pk) serializer = serializers.ScriptDetailSerializer(script, context={'request': request}) return Response(serializer.data) - def post(self, request, pk): + @extend_schema( + operation_id='extras_scripts_run', + request=serializers.ScriptInputSerializer, + responses={ + 200: OpenApiResponse( + response=serializers.ScriptDetailSerializer, + description=_("The script has been enqueued for execution."), + ), + }, + ) + def run(self, request, pk, **kwargs): """ Run a Script identified by its numeric PK or module & name and return the pending Job as the result """ + # Bound to POST on the detail route by ScriptRouter - # Running a script is a state-changing operation. If token authentication is in use, enforce the token's - # write ability before performing any object lookup. Session-authenticated requests are unaffected - # (request.auth is not a Token). + # Reject read-only tokens before resolving the script, so that an insufficient token is always + # reported as such. (Not via TokenWritePermission, which permits token auth only.) if isinstance(request.auth, Token) and not request.auth.write_enabled: - raise PermissionDenied("This token does not permit write operations.") + raise PermissionDenied(_("This token does not permit write operations (running a script).")) - script = self._get_script(pk) + # An unauthenticated user can never run a script; report that explicitly, as restrict() below would + # match no scripts and yield a misleading 404. + if not request.user.is_authenticated: + raise PermissionDenied(_("This user does not have permission to run this script.")) - if not request.user.has_perm('extras.run_script', obj=script): - raise PermissionDenied("This user does not have permission to run this script.") + # Running a script is a 'run' operation (not the 'add' that BaseViewSet maps to POST), so restrict + # the QuerySet on 'run' before resolving the script. A script the user cannot run yields a 404. + self.queryset = self.queryset.model.objects.restrict(request.user, 'run') + self.script = script = self._get_script(pk) - input_serializer = serializers.ScriptInputSerializer( - data=request.data, - context={'script': script} - ) + # A script whose Python class cannot be resolved (e.g. its module has been modified or the script has + # been deleted, retaining the record for its jobs) cannot be run + if not script.is_executable or script.python_class is None: + raise ValidationError(_("This script is not currently executable.")) + + input_serializer = self.get_serializer(data=request.data) # Check that at least one RQ worker is running if not any_workers_for_queue('default'): diff --git a/netbox/extras/models/scripts.py b/netbox/extras/models/scripts.py index 02f3902d1..042744fc6 100644 --- a/netbox/extras/models/scripts.py +++ b/netbox/extras/models/scripts.py @@ -3,7 +3,8 @@ import logging from functools import cached_property from django.contrib.contenttypes.fields import GenericRelation -from django.db import models +from django.contrib.contenttypes.models import ContentType +from django.db import models, router, transaction from django.db.models import Q from django.urls import reverse from django.utils.translation import gettext_lazy as _ @@ -11,7 +12,7 @@ from django.utils.translation import gettext_lazy as _ from core.choices import ManagedFileRootPathChoices from core.models import ManagedFile from extras.utils import is_script -from netbox.models.features import EventRulesMixin, JobsMixin +from netbox.models.features import EventRulesMixin, JobsMixin, batch_delete_jobs from utilities.querysets import RestrictedQuerySet from .mixins import PythonModuleMixin @@ -119,6 +120,29 @@ class ScriptModule(PythonModuleMixin, JobsMixin, ManagedFile): def __str__(self): return self.python_name + def delete(self, using=None, *args, **kwargs): + # Job is imported here rather than at module level to avoid a circular import + # (core.models.jobs -> core.signals -> extras.events -> extras.models -> this module). + from core.models import Job + + # Deleting a ScriptModule cascades (via the Script.module FK) to its child Scripts, and + # Django's collector would materialize every one of those Scripts' Jobs to delete them. + # A module's scripts can accumulate thousands of jobs, exhausting memory. Batch-delete + # the child Scripts' jobs up front, in a single queryset (no per-script loop), before + # delegating to the cascade. The transaction rolls the job deletions back if the parent + # delete fails; note it does not cover ManagedFile.delete removing the file from disk, + # which happens before the DB delete and is not transactional. See #22812. + using = using or router.db_for_write(self.__class__, instance=self) + with transaction.atomic(using=using): + script_type = ContentType.objects.get_for_model(Script, for_concrete_model=False) + child_jobs = Job.objects.using(using).filter( + object_type=script_type, + object_id__in=self.scripts.values_list('pk', flat=True), + ) + batch_delete_jobs(child_jobs) + return super().delete(using, *args, **kwargs) + delete.alters_data = True + @property def ordered_scripts(self): script_objects = {s.name: s for s in self.scripts.all()} diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index c7e85b903..bdac01ca0 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -8,13 +8,14 @@ from unittest.mock import MagicMock, patch from django.contrib.contenttypes.models import ContentType from django.core.files.uploadedfile import SimpleUploadedFile from django.db import IntegrityError +from django.test import override_settings from django.urls import reverse from django.utils.timezone import make_aware, now from rest_framework import status from core.choices import ManagedFileRootPathChoices from core.events import * -from core.models import DataFile, DataSource, ObjectType +from core.models import DataFile, DataSource, Job, ObjectType from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Rack, RackRole, Site from extras.api.serializers import EventRuleSerializer from extras.choices import * @@ -26,7 +27,7 @@ from netbox.registry import registry from users.constants import TOKEN_PREFIX from users.models import Group, ObjectPermission, Token, User from utilities.tables import get_table_for_model -from utilities.testing import APITestCase, APIViewTestCases +from utilities.testing import APITestCase, APIViewTestCases, disable_warnings class AppTestCase(APITestCase): @@ -1532,6 +1533,34 @@ class ScriptTestCase(APITestCase): self.assertEqual(response.data['vars']['var2'], 'IntegerVar') self.assertEqual(response.data['vars']['var3'], 'BooleanVar') + def test_list_scripts(self): + """ + The list route is served by BaseViewSet, which resolves the QuerySet's prefetches & annotations (and + any fields/omit request parameters) from the serializer. + """ + url = reverse('extras-api:script-list') + + response = self.client.get(url, **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 1) + self.assertEqual(response.data['results'][0]['name'], self.TestScriptClass.Meta.name) + + response = self.client.get(f'{url}?fields=id,name', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(sorted(response.data['results'][0]), ['id', 'name']) + + def test_get_script_by_module_and_name(self): + """ + A script may also be identified by its module & name, e.g. /api/extras/scripts/example.MyReport/. + """ + script = Script.objects.first() + url = reverse('extras-api:script-detail', kwargs={'pk': f'script.{script.name}'}) + + response = self.client.get(url, **self.header) + + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response.data['id'], script.pk) + def test_schedule_script_past_time_rejected(self): """ Scheduling with past schedule_at should fail. @@ -1602,45 +1631,216 @@ class ScriptTestCase(APITestCase): # Restore the original setting for other tests self.TestScriptClass.Meta.scheduling_enabled = original - def test_run_token_write_enabled(self): + def test_run_script_without_permission(self): """ - Running a script is an unsafe (state-changing) action and must be rejected when the calling token has - write_enabled=False, even if the user holds the run_script permission. + A user permitted to view a script but not to run it must not be able to enqueue it. (The script is + excluded from the restricted QuerySet, so the request yields a 404.) + """ + payload = {'data': {'var1': 'hello', 'var2': 1, 'var3': False}, 'commit': True} + + # setUp() grants only extras.view_script + with disable_warnings('django.request'): + response = self.client.post(self.url, payload, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_404_NOT_FOUND) + self.assertFalse(Job.objects.exists()) + + # Granting the run permission permits the same request + self.add_permissions('extras.run_script') + response = self.client.post(self.url, payload, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertTrue(Job.objects.exists()) + + @override_settings(LOGIN_REQUIRED=False, EXEMPT_VIEW_PERMISSIONS=['*']) + def test_run_script_anonymous(self): + """ + An unauthenticated user must be told that running a script is not permitted, rather than that the + script does not exist. + """ + payload = {'data': {'var1': 'hello', 'var2': 1, 'var3': False}, 'commit': True} + + with disable_warnings('django.request'): + response = self.client.post(self.url, payload, format='json') + self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN) + self.assertFalse(Job.objects.exists()) + + def test_run_script_read_only_token(self): + """ + Running a script is a write operation and must be rejected for a read-only token. """ self.add_permissions('extras.run_script') - payload = { - 'data': {'var1': 'hello', 'var2': 1, 'var3': False}, - 'commit': True, - } + payload = {'data': {'var1': 'hello', 'var2': 1, 'var3': False}, 'commit': True} - # A token with write_enabled=False should be rejected - token = Token.objects.create(version=2, user=self.user, write_enabled=False) - token_header = f'Bearer {TOKEN_PREFIX}{token.key}.{token.token}' - response = self.client.post(self.url, payload, format='json', HTTP_AUTHORIZATION=token_header) + # A write-disabled token should be rejected + ro_token = Token.objects.create(version=2, user=self.user, write_enabled=False) + ro_header = {'HTTP_AUTHORIZATION': f'Bearer {TOKEN_PREFIX}{ro_token.key}.{ro_token.token}'} + response = self.client.post(self.url, payload, format='json', **ro_header) self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN) - # Enabling write ability on the token should allow the script to run - token.write_enabled = True - token.save() - response = self.client.post(self.url, payload, format='json', HTTP_AUTHORIZATION=token_header) + # The default (write-enabled) token should succeed + response = self.client.post(self.url, payload, format='json', **self.header) self.assertHttpStatus(response, status.HTTP_200_OK) - def test_run_session_auth(self): + def test_run_script_read_only_token_without_permission(self): + """ + A read-only token is rejected before the script is resolved, so an insufficient token is reported as + such regardless of the user's permission to run the script. + """ + payload = {'data': {'var1': 'hello', 'var2': 1, 'var3': False}, 'commit': True} + + # setUp() grants only extras.view_script + ro_token = Token.objects.create(version=2, user=self.user, write_enabled=False) + ro_header = {'HTTP_AUTHORIZATION': f'Bearer {TOKEN_PREFIX}{ro_token.key}.{ro_token.token}'} + response = self.client.post(self.url, payload, format='json', **ro_header) + self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN) + + def test_run_script_session_auth(self): """ The token write-ability check applies only to token authentication. Session-authenticated requests - (where request.auth is not a Token) must still be allowed to run scripts. + (where request.auth is not a Token) must still be permitted to run scripts. """ self.add_permissions('extras.run_script') - payload = { - 'data': {'var1': 'hello', 'var2': 1, 'var3': False}, - 'commit': True, - } + payload = {'data': {'var1': 'hello', 'var2': 1, 'var3': False}, 'commit': True} # Authenticate via session rather than a token; request.auth is None self.client.force_authenticate(user=self.user) response = self.client.post(self.url, payload, format='json') self.assertHttpStatus(response, status.HTTP_200_OK) + def test_run_script_not_executable(self): + """ + A script whose Python class cannot be resolved must be rejected, not raise an exception. + """ + self.add_permissions('extras.run_script') + payload = {'data': {'var1': 'hello', 'var2': 1, 'var3': False}, 'commit': True} + + # Simulate a script whose class can no longer be found in its module + class_patch = patch.object(Script, 'python_class', None) + class_patch.start() + self.addCleanup(class_patch.stop) + + response = self.client.post(self.url, payload, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + self.assertFalse(Job.objects.exists()) + + def test_run_script_by_module_and_name(self): + """ + A script identified by its module & name (rather than by its PK) must also be runnable. + """ + self.add_permissions('extras.run_script') + payload = {'data': {'var1': 'hello', 'var2': 1, 'var3': False}, 'commit': True} + script = Script.objects.first() + url = reverse('extras-api:script-detail', kwargs={'pk': f'script.{script.name}'}) + + response = self.client.post(url, payload, format='json', **self.header) + + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response.data['id'], script.pk) + self.assertTrue(Job.objects.exists()) + + def test_run_script_format_suffix(self): + """ + The format-suffix variants of the detail route (e.g. /1.json) must dispatch to run(). + """ + self.add_permissions('extras.run_script') + payload = {'data': {'var1': 'hello', 'var2': 1, 'var3': False}, 'commit': True} + script = Script.objects.first() + lookups = (script.pk, f'script.{script.name}') + + for lookup in lookups: + with self.subTest(lookup=lookup): + url = reverse('extras-api:script-detail', kwargs={'pk': lookup, 'format': 'json'}) + + response = self.client.post(url, payload, format='json', **self.header) + + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response.data['id'], script.pk) + + self.assertEqual(Job.objects.count(), len(lookups)) + + def test_modify_script_methods_disabled(self): + """ + Individual scripts are created, modified, and deleted through their module, so PUT/PATCH/DELETE on + the script endpoint are not supported (even for a user holding the corresponding permissions). + """ + self.add_permissions('extras.change_script', 'extras.delete_script') + script = Script.objects.first() + + for method in ('put', 'patch', 'delete'): + with self.subTest(method=method): + with disable_warnings('django.request'): + response = getattr(self.client, method)(self.url, {}, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_405_METHOD_NOT_ALLOWED) + + # The script must remain untouched + self.assertTrue(Script.objects.filter(pk=script.pk).exists()) + + def test_create_script_disabled(self): + """ + Scripts cannot be created via the API: POST is mapped only on the detail route (to run a script), + and must be neither permitted nor advertised on the list route. + """ + self.add_permissions('extras.add_script') + list_url = reverse('extras-api:script-list') + + with disable_warnings('django.request'): + response = self.client.post(list_url, {}, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_405_METHOD_NOT_ALLOWED) + + # OPTIONS must not advertise a create action for the list route + response = self.client.options(list_url, **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertNotIn('POST', response.data.get('actions', {})) + + def test_options_detail_route(self): + """ + POST on the detail route runs a script, so its OPTIONS metadata must describe the run input + rather than the Script model's own fields. + """ + self.add_permissions('extras.run_script') + + response = self.client.options(self.url, **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + post_fields = response.data['actions']['POST'] + self.assertIn('data', post_fields) + self.assertIn('commit', post_fields) + self.assertNotIn('module', post_fields) + self.assertNotIn('name', post_fields) + + def test_options_detail_route_dynamic_fields(self): + """ + The run input serializer does not support the fields/omit query parameters, but their presence must + not break the generation of OPTIONS metadata. + """ + self.add_permissions('extras.run_script') + + for query in ('fields=id', 'omit=id'): + with self.subTest(query=query): + response = self.client.options(f'{self.url}?{query}', **self.header) + + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertIn('data', response.data['actions']['POST']) + + def test_unsupported_method(self): + """ + A request using an HTTP method which maps to no action must be rejected with a 405. + """ + with disable_warnings('django.request'): + response = self.client.trace(self.url, **self.header) + self.assertHttpStatus(response, status.HTTP_405_METHOD_NOT_ALLOWED) + + def test_get_script_invalid_pk(self): + """ + A PK which cannot be cast to an integer must yield a 404, not a server error. This covers numeric (but + non-decimal) characters, as well as a decimal value too long for Python to convert. + """ + for pk in ('½', '1' * 5000): + with self.subTest(pk=pk[:10]): + url = reverse('extras-api:script-detail', kwargs={'pk': pk}) + + with disable_warnings('django.request'): + response = self.client.get(url, **self.header) + self.assertHttpStatus(response, status.HTTP_404_NOT_FOUND) + class CreatedUpdatedFilterTestCase(APITestCase): diff --git a/netbox/extras/tests/test_api_routers.py b/netbox/extras/tests/test_api_routers.py new file mode 100644 index 000000000..25c791511 --- /dev/null +++ b/netbox/extras/tests/test_api_routers.py @@ -0,0 +1,52 @@ +from django.test import TestCase + +from extras.api.routers import ScriptRouter +from extras.api.views import CustomFieldChoiceSetViewSet, ScriptViewSet, WebhookViewSet + + +class ScriptRouterTestCase(TestCase): + """ + Verify the routes generated by ScriptRouter. + """ + @staticmethod + def get_actions(viewset): + """ + Return a mapping of route name to the HTTP methods bound on it for the given ViewSet. + """ + router = ScriptRouter() + router.register('dummy', viewset, basename='dummy') + + return { + url.name: url.callback.actions + for url in router.urls if hasattr(url.callback, 'actions') + } + + def test_script_routes(self): + actions = self.get_actions(ScriptViewSet) + + # POST on the detail route runs the script; the list route accepts only GET + self.assertEqual(actions['dummy-detail'], {'get': 'retrieve', 'post': 'run'}) + self.assertEqual(actions['dummy-list'], {'get': 'list'}) + + def test_script_viewset_subclass(self): + # A subclass of ScriptViewSet (e.g. as registered by a plugin) gets the same route mapping + class MyScriptViewSet(ScriptViewSet): + pass + + actions = self.get_actions(MyScriptViewSet) + + self.assertEqual(actions['dummy-detail'], {'get': 'retrieve', 'post': 'run'}) + self.assertEqual(actions['dummy-list'], {'get': 'list'}) + + def test_other_viewsets_unaffected(self): + # Standard ViewSets keep the stock detail route mapping + self.assertNotIn('post', self.get_actions(WebhookViewSet)['dummy-detail']) + + # Routes generated for @action methods are untouched + self.assertEqual(self.get_actions(CustomFieldChoiceSetViewSet)['dummy-choices'], {'get': 'choices'}) + + def test_route_templates_not_mutated(self): + router = ScriptRouter() + router.get_routes(ScriptViewSet) + + self.assertNotIn('post', router.routes[2].mapping) diff --git a/netbox/extras/tests/test_scripts_deletion.py b/netbox/extras/tests/test_scripts_deletion.py new file mode 100644 index 000000000..7419de359 --- /dev/null +++ b/netbox/extras/tests/test_scripts_deletion.py @@ -0,0 +1,306 @@ +import uuid +from unittest import mock + +from django.contrib.contenttypes.models import ContentType +from django.db import router +from django.db.models import QuerySet +from django.test import TestCase, override_settings +from django.urls import reverse + +from core.choices import JobStatusChoices, ManagedFileRootPathChoices +from core.models import DataSource, Job +from extras.models import Script, ScriptModule +from extras.validators import CustomValidator +from netbox.models.deletion import ConfirmCollector, CountOnly +from utilities.exceptions import AbortRequest +from utilities.testing import TestCase as ViewTestCase + + +class ScriptDeletionTestCase(TestCase): + """ + Regression tests for #22812: deleting a JobsMixin object (Script, ScriptModule, DataSource) + with many associated Jobs must not load every Job into memory at once. + """ + @classmethod + def setUpTestData(cls): + cls.script_ct = ContentType.objects.get_for_model(Script, for_concrete_model=False) + + def _create_module(self): + return ScriptModule.objects.create( + file_root=ManagedFileRootPathChoices.SCRIPTS, + file_path=f'test_{uuid.uuid4().hex[:8]}.py', + ) + + def _create_script(self, module=None): + module = module or self._create_module() + script = Script.objects.create(module=module, name=f'S{uuid.uuid4().hex[:8]}') + return module, script + + def _add_jobs(self, obj, count, object_type=None): + object_type = object_type or ContentType.objects.get_for_model(type(obj), for_concrete_model=False) + Job.objects.bulk_create([ + Job( + object_type=object_type, + object_id=obj.pk, + name='testjob', + status=JobStatusChoices.STATUS_COMPLETED, + job_id=uuid.uuid4(), + data={'output': 'x' * 50}, + ) + for _ in range(count) + ]) + + def test_delete_script_deletes_all_jobs(self): + _, script = self._create_script() + self._add_jobs(script, 2500) + self.assertEqual(script.jobs.count(), 2500) + + script.delete() + + self.assertFalse(Script.objects.filter(pk=script.pk).exists()) + self.assertEqual(Job.objects.filter(object_type=self.script_ct, object_id=script.pk).count(), 0) + + def test_delete_script_batches_jobs(self): + _, script = self._create_script() + self._add_jobs(script, 5) + + job_delete_calls = [] + original_delete = QuerySet.delete + + def counting_delete(qs, *args, **kwargs): + if qs.model is Job: + job_delete_calls.append(len(qs)) + return original_delete(qs, *args, **kwargs) + + with mock.patch('netbox.models.features.JOB_DELETE_BATCH_SIZE', 2): + with mock.patch.object(QuerySet, 'delete', counting_delete): + script.delete() + + # 5 jobs at a batch size of 2 => three batched deletes (2, 2, 1) + self.assertEqual(job_delete_calls, [2, 2, 1]) + self.assertEqual(Job.objects.filter(object_type=self.script_ct, object_id=script.pk).count(), 0) + + def test_delete_scriptmodule_cascades_to_scripts_and_jobs(self): + module, script = self._create_script() + self._add_jobs(script, 100) + + module.delete() + + self.assertFalse(ScriptModule.objects.filter(pk=module.pk).exists()) + self.assertFalse(Script.objects.filter(pk=script.pk).exists()) + self.assertEqual(Job.objects.filter(object_type=self.script_ct, object_id=script.pk).count(), 0) + + def test_delete_scriptmodule_batches_child_script_jobs(self): + # The reporter's actual path: a script is only removable via the UI by deleting its + # ScriptModule. The module's delete must batch the child Script's jobs. + module, script = self._create_script() + self._add_jobs(script, 5) + + job_delete_calls = [] + original_delete = QuerySet.delete + + def counting_delete(qs, *args, **kwargs): + if qs.model is Job: + job_delete_calls.append(len(qs)) + return original_delete(qs, *args, **kwargs) + + with mock.patch('netbox.models.features.JOB_DELETE_BATCH_SIZE', 2): + with mock.patch.object(QuerySet, 'delete', counting_delete): + module.delete() + + # 5 child-script jobs at a batch size of 2 => three batched deletes (2, 2, 1). The module + # has no jobs of its own, so JobsMixin.delete adds no further Job deletes. + self.assertEqual(job_delete_calls, [2, 2, 1]) + self.assertFalse(Script.objects.filter(pk=script.pk).exists()) + self.assertEqual(Job.objects.filter(object_type=self.script_ct, object_id=script.pk).count(), 0) + + def test_delete_datasource_deletes_jobs(self): + datasource = DataSource.objects.create(name='DS', type='local', source_url='/tmp/test') + self._add_jobs(datasource, 100) + ds_ct = ContentType.objects.get_for_model(DataSource, for_concrete_model=False) + self.assertEqual(Job.objects.filter(object_type=ds_ct, object_id=datasource.pk).count(), 100) + + datasource.delete() + + self.assertFalse(DataSource.objects.filter(pk=datasource.pk).exists()) + self.assertEqual(Job.objects.filter(object_type=ds_ct, object_id=datasource.pk).count(), 0) + + def test_soft_delete_preserves_jobs(self): + _, script = self._create_script() + self._add_jobs(script, 10) + + script.delete(soft_delete=True) + + script.refresh_from_db() + self.assertFalse(script.is_executable) + self.assertEqual(Job.objects.filter(object_type=self.script_ct, object_id=script.pk).count(), 10) + + @override_settings(PROTECTION_RULES={'extras.script': [CustomValidator({'name': {'eq': ''}})]}) + def test_delete_rolls_back_jobs_on_parent_failure(self): + # A protection rule that no real script can satisfy (name must be empty) makes the + # cascade's pre_delete handler raise AbortRequest *after* JobsMixin.delete has already + # batch-deleted the jobs. JobsMixin.delete wraps the batch loop and super().delete() in a + # transaction, so the job deletions must roll back, leaving no orphaned partial state. + # This exercises the real deletion-abort path rather than mocking Django internals. + _, script = self._create_script() + self._add_jobs(script, 10) + + with self.assertRaises(AbortRequest): + script.delete() + + self.assertTrue(Script.objects.filter(pk=script.pk).exists()) + self.assertEqual(Job.objects.filter(object_type=self.script_ct, object_id=script.pk).count(), 10) + + @override_settings(PROTECTION_RULES={'extras.script': [CustomValidator({'name': {'eq': ''}})]}) + def test_delete_scriptmodule_rolls_back_child_jobs_on_failure(self): + # Same abort path via the module: the protection rule fires when the cascade pre_deletes + # the child Script, after ScriptModule.delete has already batch-deleted that script's jobs. + # The transaction must roll those job deletions back, leaving no orphaned partial state. + module, script = self._create_script() + self._add_jobs(script, 10) + + with self.assertRaises(AbortRequest): + module.delete() + + self.assertTrue(ScriptModule.objects.filter(pk=module.pk).exists()) + self.assertTrue(Script.objects.filter(pk=script.pk).exists()) + self.assertEqual(Job.objects.filter(object_type=self.script_ct, object_id=script.pk).count(), 10) + + +class ConfirmCollectorTestCase(TestCase): + """ + #22812: the delete-confirmation page must not materialize every dependent Job. + """ + def _create_script_with_jobs(self, count): + module = ScriptModule.objects.create( + file_root=ManagedFileRootPathChoices.SCRIPTS, + file_path=f'test_{uuid.uuid4().hex[:8]}.py', + ) + script = Script.objects.create(module=module, name=f'S{uuid.uuid4().hex[:8]}') + ct = ContentType.objects.get_for_model(Script, for_concrete_model=False) + Job.objects.bulk_create([ + Job(object_type=ct, object_id=script.pk, name='j', status=JobStatusChoices.STATUS_COMPLETED, + job_id=uuid.uuid4(), data={'output': 'x' * 50}) + for _ in range(count) + ]) + return script + + def test_confirm_collector_counts_jobs_without_instantiating(self): + script = self._create_script_with_jobs(500) + + init_calls = [] + original_init = Job.__init__ + + def counting_init(self, *args, **kwargs): + init_calls.append(1) + original_init(self, *args, **kwargs) + + with mock.patch.object(Job, '__init__', counting_init): + collector = ConfirmCollector(using=router.db_for_write(Script)) + collector.collect([script]) + + # No Job rows were instantiated; the relation was counted instead. + self.assertEqual(len(init_calls), 0) + self.assertNotIn(Job, collector.data) + self.assertEqual(collector.generic_relation_counts.get(Job), 500) + # The non-job cascade (the Script itself) is still collected. + self.assertIn(Script, collector.data) + + def test_count_only_wrapper(self): + # CountOnly reports its count via len() but iterates empty, so it slots into the + # dependent-objects mapping as a non-expandable, non-materializing row. + wrapper = CountOnly(3000) + self.assertEqual(len(wrapper), 3000) + self.assertEqual(list(wrapper), []) + self.assertTrue(wrapper.count_only) + + def test_confirm_collector_omits_jobs_when_none(self): + # A jobless object must not record a zero count, or the confirmation page would show a + # spurious "0 jobs" row (#22812 regression). + datasource = DataSource.objects.create(name='DS', type='local', source_url='/tmp/test') + + collector = ConfirmCollector(using=router.db_for_write(DataSource)) + collector.collect([datasource]) + + self.assertNotIn(Job, collector.generic_relation_counts) + + +class ObjectDeleteViewCountsTestCase(ViewTestCase): + """ + #22812: the delete-confirmation view must report a JobsMixin object's jobs as a count + (via CountOnly) without materializing them, and _get_dependent_objects must keep returning + a single dict. + """ + def test_get_dependent_objects_returns_count_only_for_jobs(self): + from netbox.views.generic.object_views import ObjectDeleteView + + module = ScriptModule.objects.create( + file_root=ManagedFileRootPathChoices.SCRIPTS, + file_path=f'test_{uuid.uuid4().hex[:8]}.py', + ) + script = Script.objects.create(module=module, name=f'S{uuid.uuid4().hex[:8]}') + ct = ContentType.objects.get_for_model(Script, for_concrete_model=False) + Job.objects.bulk_create([ + Job( + object_type=ct, object_id=script.pk, name='j', + status=JobStatusChoices.STATUS_COMPLETED, job_id=uuid.uuid4(), + ) + for _ in range(50) + ]) + + view = ObjectDeleteView() + view.queryset = ScriptModule.objects.all() + dependent_objects = view._get_dependent_objects(module) + + # Single dict returned (not a tuple); jobs represented as a CountOnly. + self.assertIsInstance(dependent_objects, dict) + self.assertIn(Job, dependent_objects) + self.assertIsInstance(dependent_objects[Job], CountOnly) + self.assertEqual(len(dependent_objects[Job]), 50) + + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_confirm_page_renders_job_count(self): + module = ScriptModule.objects.create( + file_root=ManagedFileRootPathChoices.SCRIPTS, + file_path=f'test_{uuid.uuid4().hex[:8]}.py', + ) + script = Script.objects.create(module=module, name=f'S{uuid.uuid4().hex[:8]}') + ct = ContentType.objects.get_for_model(Script, for_concrete_model=False) + Job.objects.bulk_create([ + Job( + object_type=ct, object_id=script.pk, name='j', + status=JobStatusChoices.STATUS_COMPLETED, job_id=uuid.uuid4(), + ) + for _ in range(50) + ]) + + # ScriptModule is a proxy over core.ManagedFile, so the delete view requires the + # concrete model's permission (core.delete_managedfile), not extras.delete_scriptmodule. + self.add_permissions('core.delete_managedfile') + url = reverse('extras:scriptmodule_delete', kwargs={'pk': module.pk}) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + # Assert on the rendered context, not brittle HTML substrings: Job is present in + # dependent_objects as a CountOnly reporting the true count, so the confirmation page + # renders it as a summarized (non-expandable) row without materializing 50 Job rows. + dependent_objects = response.context['dependent_objects'] + self.assertIn(Job, dependent_objects) + self.assertIsInstance(dependent_objects[Job], CountOnly) + self.assertEqual(len(dependent_objects[Job]), 50) + self.assertTrue(dependent_objects[Job].count_only) + + def test_get_dependent_objects_omits_jobs_when_none(self): + from netbox.views.generic.object_views import ObjectDeleteView + + # A module with no jobs must not produce a CountOnly(0) entry (#22812 regression). + module = ScriptModule.objects.create( + file_root=ManagedFileRootPathChoices.SCRIPTS, + file_path=f'test_{uuid.uuid4().hex[:8]}.py', + ) + + view = ObjectDeleteView() + view.queryset = ScriptModule.objects.all() + dependent_objects = view._get_dependent_objects(module) + + self.assertNotIn(Job, dependent_objects) diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 6bcd3d4f5..8ecf09457 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -1246,7 +1246,7 @@ class ServicePortMappingFilterMixin(django_filters.FilterSet): ) protocol = django_filters.MultipleChoiceFilter( choices=ServiceProtocolChoices, - method='filter_noop', + method='filter_protocol_noop', ) # Negation lookup retained from when `protocol` was a model field: method-based filters don't get # the char-based lookups (protocol__n, __ic, ...) auto-generated, and silently dropping protocol__n @@ -1314,6 +1314,13 @@ class ServicePortMappingFilterMixin(django_filters.FilterSet): # See filter_queryset(), which applies `protocol` and the port lookups as one correlated predicate. return queryset + def filter_protocol_noop(self, queryset, name, value: list[str]): + # A no-op like filter_noop(), from which this differs only in its type hint. `protocol` is not a + # model field, so the schema generator cannot infer the parameter's type from the model, and a + # plain MultipleChoiceFilter carries none of the @extend_schema_field annotations which the + # MultiValue* filters (used by the port lookups) do. The hint supplies it. + return self.filter_noop(queryset, name, value) + def filter_port_mappings(self, queryset, name, value: list[str]): # Array overlap (&&) is served by the GIN index on port_mappings and gives the multi-value OR # semantics used throughout NetBox: ?port_mappings=tcp/80&port_mappings=udp/53 matches either. diff --git a/netbox/netbox/api/viewsets/__init__.py b/netbox/netbox/api/viewsets/__init__.py index c814fbf47..50af77097 100644 --- a/netbox/netbox/api/viewsets/__init__.py +++ b/netbox/netbox/api/viewsets/__init__.py @@ -7,6 +7,7 @@ from django.db import router, transaction from django.db.models import ProtectedError, RestrictedError from rest_framework import mixins as drf_mixins from rest_framework import status +from rest_framework.exceptions import MethodNotAllowed from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet @@ -87,6 +88,13 @@ class BaseViewSet(GenericViewSet): def initial(self, request, *args, **kwargs): super().initial(request, *args, **kwargs) + # Reject any method for which no action has been declared, rather than proceeding against an + # unrestricted QuerySet. (A method mapped to None, e.g. OPTIONS, is permitted: it needs no + # restriction.) This is the same 405 DRF would return when resolving the handler for an unmapped + # method, but it also covers a handler bound to such a method (e.g. @action(methods=['trace'])). + if request.method not in HTTP_ACTIONS: + raise MethodNotAllowed(request.method) + # Restrict the view's QuerySet to allow only the permitted objects if request.user.is_authenticated: if action := HTTP_ACTIONS[request.method]: diff --git a/netbox/netbox/constants.py b/netbox/netbox/constants.py index ab741d2f9..64afe4bca 100644 --- a/netbox/netbox/constants.py +++ b/netbox/netbox/constants.py @@ -39,3 +39,10 @@ CENSOR_TOKEN_CHANGED = '***CHANGED***' # Placeholder text for empty tables EMPTY_TABLE_TEXT = 'No results found' + +# Batch size for deleting a JobsMixin object's associated jobs during cascade deletion. Job +# cannot be fast-deleted (a global pre_delete receiver forces per-instance signals), so deleting +# in chunks bounds the work per delete cycle rather than building one huge collection and running +# one long DELETE. 1000 matches EXPORT_CHUNK_SIZE and, in benchmarking a 200k-job deletion, was +# the fastest of 100/1000/5000 while keeping peak memory flat. See #22812. +JOB_DELETE_BATCH_SIZE = 1000 diff --git a/netbox/netbox/context_managers.py b/netbox/netbox/context_managers.py index c0ac9c2d7..5155a63d8 100644 --- a/netbox/netbox/context_managers.py +++ b/netbox/netbox/context_managers.py @@ -15,17 +15,20 @@ def event_tracking(request): :param request: WSGIRequest object with a unique `id` set """ - current_request.set(request) - events_queue.set({}) - query_cache.set(defaultdict(dict)) + request_token = current_request.set(request) + queue_token = events_queue.set({}) + cache_token = query_cache.set(defaultdict(dict)) - yield + try: + yield - # Flush queued webhooks to RQ - if events := list(events_queue.get().values()): - flush_events(events) + # Flush queued webhooks to RQ. This is done only if the wrapped block completed successfully; events + # queued by a failed request or job must not be dispatched. + if events := list(events_queue.get().values()): + flush_events(events) - # Clear context vars - current_request.set(None) - events_queue.set({}) - query_cache.set(None) + finally: + # Restore the previous context vars, whether or not the wrapped block raised an exception + current_request.reset(request_token) + events_queue.reset(queue_token) + query_cache.reset(cache_token) diff --git a/netbox/netbox/forms/bulk_import.py b/netbox/netbox/forms/bulk_import.py index 6bd50442b..730d28084 100644 --- a/netbox/netbox/forms/bulk_import.py +++ b/netbox/netbox/forms/bulk_import.py @@ -1,4 +1,5 @@ from django import forms +from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from django.db import models from django.utils.translation import gettext_lazy as _ @@ -70,6 +71,31 @@ class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm): return cleaned + def _update_errors(self, errors): + """Convert errors for fields absent from the form to prefixed non-field errors.""" + if hasattr(errors, 'error_dict'): + remapped = [] + passthrough = {} + for field, error_list in errors.error_dict.items(): + if field == NON_FIELD_ERRORS or field in self.fields: + passthrough[field] = error_list + else: + for error in error_list: + message = next(iter(error)) + if error.params: + message = message.replace('%', '%%') + remapped.append(ValidationError( + '{field}: {message}'.format(field=field, message=message), + code=error.code, + params=error.params, + )) + if passthrough: + super()._update_errors(ValidationError(passthrough)) + for error in remapped: + self.add_error(None, error) + else: + super()._update_errors(errors) + class OwnerCSVMixin(forms.Form): owner = CSVModelChoiceField( diff --git a/netbox/netbox/models/deletion.py b/netbox/netbox/models/deletion.py index e87321ae3..b0600e9e2 100644 --- a/netbox/netbox/models/deletion.py +++ b/netbox/netbox/models/deletion.py @@ -8,6 +8,72 @@ from django.utils.translation import gettext as _ logger = logging.getLogger("netbox.models.deletion") +class CountOnly: + """ + A stand-in for a list of dependent instances that reports a count without holding any + instances. Used on the delete-confirmation page for high-cardinality relations (e.g. a + JobsMixin object's jobs) which we deliberately do not materialize (see #22812). It is a + lenient, empty iterable: `len()` returns the true row count, but iterating yields nothing, + so it slots into the same `{model: }` mapping as real instance lists and renders + as a non-expandable row. + """ + # Template flag: distinguishes a count-only entry (no instances to list) from a real list, + # so the confirmation page can render it without an expand/collapse affordance. + count_only = True + + def __init__(self, count): + self.count = count + + def __len__(self): + return self.count + + def __iter__(self): + return iter(()) + + +class ConfirmCollector(Collector): + """ + A display-only Collector used to enumerate the objects that would be deleted along with a + given object, for rendering the delete confirmation page. It behaves like Django's stock + Collector (preserving the full FK cascade graph and its ProtectedError/RestrictedError + behavior) except that it does not descend into the `jobs` GenericRelation. A JobsMixin + object can accumulate thousands of Jobs, each carrying large data/log_entries payloads; + materializing them all just to render a confirmation page can exhaust memory (see #22812). + Instead, the related Jobs are counted and recorded in `generic_relation_counts`. + + This is intentionally specific to Job, the only high-cardinality GenericRelation in the + data model; it is not a general count-out over every GenericRelation. If another relation + ever needs the same treatment, extend the check in collect() (and the matching write-path + batching in JobsMixin/ScriptModule.delete) rather than assuming this already handles it. + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.generic_relation_counts = {} + + def collect(self, objs, source=None, *args, **kwargs): + """ + Override collect() to count the `jobs` GenericRelation rather than descend into it. + + Django's Collector offers no per-relation skip hook, so we intercept the one call it + makes when cascading into a GenericRelation: collect(sub_objs, source=model, ...), where + `sub_objs` is a queryset of the related model. When that model is Job, we count the rows + instead of collecting (and thus instantiating) them, and forward every other call to the + stock implementation untouched. A directly-deleted Job (top-level call, source=None) + still collects normally. + """ + from core.models import Job + + if source is not None and getattr(objs, 'model', None) is Job: + # Django calls this branch for the jobs relation even when there are none; only record + # a count when there are actually jobs, so jobless objects don't get a spurious + # "0 jobs" row on the delete-confirmation page. + count = objs.count() + if count: + self.generic_relation_counts[Job] = self.generic_relation_counts.get(Job, 0) + count + return None + return super().collect(objs, source=source, *args, **kwargs) + + class CustomCollector(Collector): """ Override Django's stock Collector to handle GenericRelations and ensure proper ordering of cascading deletions. diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index 003a25a5a..16ee2cce9 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -5,7 +5,7 @@ from functools import cached_property from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.core.validators import ValidationError -from django.db import models +from django.db import models, router, transaction from django.db.models import Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -17,7 +17,7 @@ from extras.constants import CUSTOMFIELD_EMPTY_VALUES from extras.managers import NetBoxTaggableManager, NetBoxTaggableManagerField from extras.utils import is_taggable from netbox.config import get_config -from netbox.constants import CORE_APPS +from netbox.constants import CORE_APPS, JOB_DELETE_BATCH_SIZE from netbox.models.deletion import DeleteMixin from netbox.plugins import PluginConfig from netbox.registry import registry @@ -43,6 +43,7 @@ __all__ = ( 'NotificationsMixin', 'SyncedDataMixin', 'TagsMixin', + 'batch_delete_jobs', 'get_model_features', 'has_feature', 'model_is_public', @@ -456,9 +457,39 @@ class NotificationsMixin(models.Model): abstract = True +def batch_delete_jobs(job_queryset): + """ + Delete the Jobs in `job_queryset` in JOB_DELETE_BATCH_SIZE chunks. Job cannot be fast-deleted + (a global pre_delete receiver forces per-instance signals), so a single delete would build one + huge collection of Job instances and run one very long DELETE; batching bounds the per-cycle + work. Callers are responsible for wrapping this in a transaction. As with the prior cascade + behavior, this bulk delete does not invoke Job.delete() and therefore does not cancel the + backing RQ job. See #22812. + """ + from core.models import Job + + # Route writes to the same database the queryset reads from. In JobsMixin.delete the queryset + # is bound to the instance's DB while Job.objects would otherwise use the router default; if + # those diverge the deleted rows never leave the read side and the loop below never terminates. + jobs = Job.objects.using(job_queryset.db) + + job_pks = job_queryset.order_by('pk').values_list('pk', flat=True) + # Re-slice the queryset each iteration: it re-queries after each batch delete, so the + # remaining set shrinks and the loop terminates (do not hoist this into a cursor). + while pks := list(job_pks[:JOB_DELETE_BATCH_SIZE]): + # only('pk'): the batch still can't fast-delete, so each Job in the batch is instantiated; + # loading just the PK avoids pulling the large data/log_entries payloads into memory. + jobs.filter(pk__in=pks).only('pk').delete() + + class JobsMixin(models.Model): """ Enables support for job results. + + Note: for the job-batching in delete() to run, JobsMixin must precede DeleteMixin in a + model's MRO. DeleteMixin.delete() drives its own collector and does not call super(), so a + model declared as e.g. `class Foo(NetBoxModel, JobsMixin)` would reach DeleteMixin first and + bypass the batching. Core models that combine both (e.g. DataSource) list JobsMixin first. """ jobs = GenericRelation( to='core.Job', @@ -470,6 +501,16 @@ class JobsMixin(models.Model): class Meta: abstract = True + def delete(self, using=None, *args, **kwargs): + # Delete associated jobs in batches so the cascade never has to load thousands of Job + # rows into memory at once. Wrapped in a transaction so that a failure in the parent + # delete rolls the job deletions back as well. See #22812. + using = using or router.db_for_write(self.__class__, instance=self) + with transaction.atomic(using=using): + batch_delete_jobs(self.jobs.using(using)) + return super().delete(using, *args, **kwargs) + delete.alters_data = True + def get_latest_jobs(self): """ Return a list of the most recent jobs for this instance. diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 2d805eb7b..ac2c7817d 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -653,10 +653,6 @@ SERIALIZATION_MODULES = { DEBUG_TOOLBAR_CONFIG = { 'SHOW_TOOLBAR_CALLBACK': 'utilities.debug.show_toolbar', - # The GraphiQL integration provided by strawberry-django locates the toolbar via - # document.getElementById('djDebug'), which fails when the toolbar is rendered inside a - # shadow root (the default as of django-debug-toolbar v7.0). - 'USE_SHADOW_DOM': False, } diff --git a/netbox/netbox/tests/test_context_managers.py b/netbox/netbox/tests/test_context_managers.py new file mode 100644 index 000000000..1261a5290 --- /dev/null +++ b/netbox/netbox/tests/test_context_managers.py @@ -0,0 +1,87 @@ +import uuid +from unittest.mock import patch + +from django.test import RequestFactory, SimpleTestCase + +from netbox.context import current_request, events_queue, query_cache +from netbox.context_managers import event_tracking + + +def _build_request(): + request = RequestFactory().get('/') + request.id = uuid.uuid4() + request.user = None + return request + + +class EventTrackingTestCase(SimpleTestCase): + """ + Verify that event_tracking() populates and restores its context variables. + """ + def assertContextVarsRestored(self): + self.assertIsNone(current_request.get()) + self.assertEqual(events_queue.get(), {}) + self.assertIsNone(query_cache.get()) + + def test_context_vars_set_within_block(self): + request = _build_request() + + with event_tracking(request): + self.assertIs(current_request.get(), request) + self.assertEqual(events_queue.get(), {}) + self.assertIsNotNone(query_cache.get()) + + self.assertContextVarsRestored() + + def test_context_vars_restored_after_exception(self): + request = _build_request() + + with self.assertRaises(RuntimeError): + with event_tracking(request): + raise RuntimeError('simulated view/script failure') + + self.assertContextVarsRestored() + + def test_events_flushed_on_success(self): + request = _build_request() + + with patch('netbox.context_managers.flush_events') as flush_events: + with event_tracking(request): + events_queue.get()['foo'] = 'bar' + + flush_events.assert_called_once_with(['bar']) + + def test_events_not_flushed_after_exception(self): + request = _build_request() + + with patch('netbox.context_managers.flush_events') as flush_events: + with self.assertRaises(RuntimeError): + with event_tracking(request): + events_queue.get()['foo'] = 'bar' + raise RuntimeError('simulated view/script failure') + + flush_events.assert_not_called() + + def test_nested_context_restores_outer_values(self): + outer_request = _build_request() + inner_request = _build_request() + + with patch('netbox.context_managers.flush_events'): + with event_tracking(outer_request): + outer_cache = query_cache.get() + outer_queue = events_queue.get() + outer_queue['outer'] = 'event' + + with event_tracking(inner_request): + self.assertIs(current_request.get(), inner_request) + self.assertIsNot(events_queue.get(), outer_queue) + self.assertEqual(events_queue.get(), {}) + + # The outer request's context must be restored intact, including any events it had + # already queued + self.assertIs(current_request.get(), outer_request) + self.assertIs(query_cache.get(), outer_cache) + self.assertIs(events_queue.get(), outer_queue) + self.assertEqual(events_queue.get(), {'outer': 'event'}) + + self.assertContextVarsRestored() diff --git a/netbox/netbox/tests/test_forms.py b/netbox/netbox/tests/test_forms.py index 0d3bda991..b2dd5c528 100644 --- a/netbox/netbox/tests/test_forms.py +++ b/netbox/netbox/tests/test_forms.py @@ -1,4 +1,9 @@ +from unittest.mock import patch + from django import forms +from django.core.exceptions import NON_FIELD_ERRORS +from django.core.exceptions import ValidationError as DjangoValidationError +from django.core.validators import MaxLengthValidator from django.forms.models import ALL_FIELDS from django.test import TestCase @@ -22,12 +27,8 @@ from vpn.forms import TunnelCreateForm, TunnelTerminationForm from wireless.forms import WirelessLANForm -class NetBoxModelImportFormCleanTestCase(TestCase): - """ - Test the clean() method of NetBoxModelImportForm to ensure it properly converts - empty strings to None for nullable fields during CSV import. - Uses InterfaceImportForm as the concrete implementation to test. - """ +class NetBoxModelImportFormTestCase(TestCase): + """Test NetBoxModelImportForm.""" @classmethod def setUpTestData(cls): @@ -319,6 +320,102 @@ class NetBoxModelImportFormCleanTestCase(TestCase): self.assertTrue(form.is_valid(), f'Form errors: {form.errors}') self.assertIsNone(form.cleaned_data['wwn']) + def test_missing_field_validation_error_becomes_non_field_error(self): + """Convert validation errors for absent fields to non-field errors.""" + form = InterfaceImportForm( + data={ + 'device': self.device, + 'name': 'Test Interface', + 'type': InterfaceTypeChoices.TYPE_1GE_GBIC, + } + ) + with patch.object( + form.instance, + 'full_clean', + side_effect=DjangoValidationError({'absent_field': ['Field error.']}), + ): + result = form.is_valid() + + self.assertFalse(result) + self.assertIn('absent_field: Field error.', form.non_field_errors()) + + def test_non_field_error_not_overwritten_by_remapped_missing_field_error(self): + """Preserve remapped and existing non-field errors.""" + form = InterfaceImportForm( + data={ + 'device': self.device, + 'name': 'Test Interface Mixed', + 'type': InterfaceTypeChoices.TYPE_1GE_GBIC, + } + ) + self.assertTrue(form.is_valid(), f'Form errors: {form.errors}') + # absent_field appears first to expose the former overwrite bug + form._update_errors(DjangoValidationError({ + 'absent_field': ['Field error.'], + NON_FIELD_ERRORS: ['A general error.'], + })) + non_field_errors = form.non_field_errors() + self.assertIn('A general error.', non_field_errors) + self.assertIn('absent_field: Field error.', non_field_errors) + + def test_remapped_error_preserves_code_and_params(self): + """Preserve the original ValidationError code and params when remapping.""" + form = InterfaceImportForm( + data={ + 'device': self.device, + 'name': 'Test Interface Params', + 'type': InterfaceTypeChoices.TYPE_1GE_GBIC, + } + ) + self.assertTrue(form.is_valid(), f'Form errors: {form.errors}') + form._update_errors(DjangoValidationError({ + 'absent_field': [ + DjangoValidationError( + '%(value)s is not a valid value.', + code='invalid_value', + params={'value': '100%'}, + ), + ], + })) + non_field_errors = form.non_field_errors() + self.assertIn( + 'absent_field: 100% is not a valid value.', + non_field_errors, + ) + error_data = form.errors[NON_FIELD_ERRORS].as_data() + matching = [error for error in error_data if error.code == 'invalid_value'] + self.assertEqual(len(matching), 1) + self.assertEqual(matching[0].params, {'value': '100%'}) + + def test_remapped_error_preserves_pluralized_message(self): + """Preserve pluralization order when remapping an ngettext_lazy message.""" + form = InterfaceImportForm( + data={ + 'device': self.device, + 'name': 'Test Interface Plural', + 'type': InterfaceTypeChoices.TYPE_1GE_GBIC, + } + ) + self.assertTrue(form.is_valid(), f'Form errors: {form.errors}') + form._update_errors(DjangoValidationError({ + 'absent_field': [ + DjangoValidationError( + MaxLengthValidator.message, + code=MaxLengthValidator.code, + params={'limit_value': 20, 'show_value': 25}, + ), + ], + })) + non_field_errors = form.non_field_errors() + self.assertIn( + 'absent_field: Ensure this value has at most 20 characters (it has 25).', + non_field_errors, + ) + error_data = form.errors[NON_FIELD_ERRORS].as_data() + matching = [error for error in error_data if error.code == MaxLengthValidator.code] + self.assertEqual(len(matching), 1) + self.assertEqual(matching[0].params, {'limit_value': 20, 'show_value': 25}) + class HTMXPartialSwapRenderingTestCase(TestCase): """Ensure each form field's HTMX swap wiring is present on the widget bound to the field.""" diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 3e9690030..e815a45cb 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -4,7 +4,6 @@ from collections import defaultdict from django.contrib import messages from django.db import router, transaction from django.db.models import ProtectedError, RestrictedError -from django.db.models.deletion import Collector from django.http import HttpResponse from django.shortcuts import redirect, render from django.urls import reverse @@ -13,6 +12,7 @@ from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ from core.signals import clear_events +from netbox.models.deletion import ConfirmCollector, CountOnly from netbox.object_actions import BulkDelete, BulkEdit, CloneObject, DeleteObject, EditObject from utilities.error_handlers import handle_protectederror from utilities.exceptions import AbortRequest, PermissionsViolation @@ -385,14 +385,19 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): def _get_dependent_objects(self, obj): """ - Returns a dictionary mapping of dependent objects (organized by model) which will be deleted as a result of - deleting the requested object. + Returns a dictionary mapping each dependent model to the objects (of that model) which will + be deleted as a result of deleting the requested object. + + Values are normally a list of instances. For high-cardinality relations that we do not + materialize to avoid excessive memory use (currently a JobsMixin object's jobs, see + #22812), the value is a `CountOnly` — a lenient empty iterable whose `len()` is the true + row count, so it renders as a non-expandable row alongside the itemized relations. Args: obj: The object to return dependent objects for """ using = router.db_for_write(obj._meta.model) - collector = Collector(using=using) + collector = ConfirmCollector(using=using) collector.collect([obj]) # Compile a mapping of models to instances @@ -406,7 +411,13 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): continue dependent_objects[model].append(instances) - return dict(dependent_objects) + # Add count-only entries for relations the collector enumerated by count rather than by + # instance (e.g. jobs), so they render as non-expandable rows in the same mapping. + dependent_objects = dict(dependent_objects) + for model, count in collector.generic_relation_counts.items(): + dependent_objects[model] = CountOnly(count) + + return dependent_objects def _handle_protected_objects(self, obj, protected_objects, request, exc): """ diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index a91a1e989..57c12d7f6 100644 --- a/netbox/project-static/dist/netbox.js +++ b/netbox/project-static/dist/netbox.js @@ -1,11 +1,11 @@ -"use strict";(()=>{var hu=Object.create;var Li=Object.defineProperty,pu=Object.defineProperties,mu=Object.getOwnPropertyDescriptor,gu=Object.getOwnPropertyDescriptors,vu=Object.getOwnPropertyNames,ys=Object.getOwnPropertySymbols,yu=Object.getPrototypeOf,Es=Object.prototype.hasOwnProperty,Eu=Object.prototype.propertyIsEnumerable;var Ur=(n,e,t)=>e in n?Li(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,O=(n,e)=>{for(var t in e||(e={}))Es.call(e,t)&&Ur(n,t,e[t]);if(ys)for(var t of ys(e))Eu.call(e,t)&&Ur(n,t,e[t]);return n},oe=(n,e)=>pu(n,gu(e));var bu=(n,e)=>()=>{try{return e||n((e={exports:{}}).exports,e),e.exports}catch(t){throw e=0,t}},bs=(n,e)=>{for(var t in e)Li(n,t,{get:e[t],enumerable:!0})},_u=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of vu(e))!Es.call(n,r)&&r!==t&&Li(n,r,{get:()=>e[r],enumerable:!(i=mu(e,r))||i.enumerable});return n};var wu=(n,e,t)=>(t=n!=null?hu(yu(n)):{},_u(e||!n||!n.__esModule?Li(t,"default",{value:n,enumerable:!0}):t,n));var se=(n,e,t)=>Ur(n,typeof e!="symbol"?e+"":e,t);var at=(n,e,t)=>new Promise((i,r)=>{var o=l=>{try{a(t.next(l))}catch(c){r(c)}},s=l=>{try{a(t.throw(l))}catch(c){r(c)}},a=l=>l.done?i(l.value):Promise.resolve(l.value).then(o,s);a((t=t.apply(n,e)).next())});var Lc=bu((gi,ss)=>{(function(e,t){typeof gi=="object"&&typeof ss=="object"?ss.exports=t():typeof define=="function"&&define.amd?define([],t):typeof gi=="object"?gi.ClipboardJS=t():e.ClipboardJS=t()})(gi,function(){return(function(){var n={686:(function(i,r,o){"use strict";o.d(r,{default:function(){return Re}});var s=o(279),a=o.n(s),l=o(370),c=o.n(l),u=o(817),d=o.n(u);function p(W){try{return document.execCommand(W)}catch(M){return!1}}var y=function(M){var D=d()(M);return p("cut"),D},m=y;function g(W){var M=document.documentElement.getAttribute("dir")==="rtl",D=document.createElement("textarea");D.style.fontSize="12pt",D.style.border="0",D.style.padding="0",D.style.margin="0",D.style.position="absolute",D.style[M?"right":"left"]="-9999px";var B=window.pageYOffset||document.documentElement.scrollTop;return D.style.top="".concat(B,"px"),D.setAttribute("readonly",""),D.value=W,D}var _=function(M,D){var B=g(M);D.container.appendChild(B);var V=d()(B);return p("copy"),B.remove(),V},x=function(M){var D=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},B="";return typeof M=="string"?B=_(M,D):M instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(M==null?void 0:M.type)?B=_(M.value,D):(B=d()(M),p("copy")),B},A=x;function w(W){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?w=function(D){return typeof D}:w=function(D){return D&&typeof Symbol=="function"&&D.constructor===Symbol&&D!==Symbol.prototype?"symbol":typeof D},w(W)}var C=function(){var M=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},D=M.action,B=D===void 0?"copy":D,V=M.container,U=M.target,Y=M.text;if(B!=="copy"&&B!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(U!==void 0)if(U&&w(U)==="object"&&U.nodeType===1){if(B==="copy"&&U.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(B==="cut"&&(U.hasAttribute("readonly")||U.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Y)return A(Y,{container:V});if(U)return B==="cut"?m(U):A(U,{container:V})},$=C;function q(W){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?q=function(D){return typeof D}:q=function(D){return D&&typeof Symbol=="function"&&D.constructor===Symbol&&D!==Symbol.prototype?"symbol":typeof D},q(W)}function H(W,M){if(!(W instanceof M))throw new TypeError("Cannot call a class as a function")}function I(W,M){for(var D=0;D0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof V.action=="function"?V.action:this.defaultAction,this.target=typeof V.target=="function"?V.target:this.defaultTarget,this.text=typeof V.text=="function"?V.text:this.defaultText,this.container=q(V.container)==="object"?V.container:document.body}},{key:"listenClick",value:function(V){var U=this;this.listener=c()(V,"click",function(Y){return U.onClick(Y)})}},{key:"onClick",value:function(V){var U=V.delegateTarget||V.currentTarget,Y=this.action(U)||"copy",ee=$({action:Y,container:this.container,target:this.target(U),text:this.text(U)});this.emit(ee?"success":"error",{action:Y,text:ee,trigger:U,clearSelection:function(){U&&U.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(V){return ne("action",V)}},{key:"defaultTarget",value:function(V){var U=ne("target",V);if(U)return document.querySelector(U)}},{key:"defaultText",value:function(V){return ne("text",V)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(V){var U=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return A(V,U)}},{key:"cut",value:function(V){return m(V)}},{key:"isSupported",value:function(){var V=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],U=typeof V=="string"?[V]:V,Y=!!document.queryCommandSupported;return U.forEach(function(ee){Y=Y&&!!document.queryCommandSupported(ee)}),Y}}]),D})(a()),Re=Ue}),828:(function(i){var r=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var o=Element.prototype;o.matches=o.matchesSelector||o.mozMatchesSelector||o.msMatchesSelector||o.oMatchesSelector||o.webkitMatchesSelector}function s(a,l){for(;a&&a.nodeType!==r;){if(typeof a.matches=="function"&&a.matches(l))return a;a=a.parentNode}}i.exports=s}),438:(function(i,r,o){var s=o(828);function a(u,d,p,y,m){var g=c.apply(this,arguments);return u.addEventListener(p,g,m),{destroy:function(){u.removeEventListener(p,g,m)}}}function l(u,d,p,y,m){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof p=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(g){return a(g,d,p,y,m)}))}function c(u,d,p,y){return function(m){m.delegateTarget=s(m.target,d),m.delegateTarget&&y.call(u,m)}}i.exports=l}),879:(function(i,r){r.node=function(o){return o!==void 0&&o instanceof HTMLElement&&o.nodeType===1},r.nodeList=function(o){var s=Object.prototype.toString.call(o);return o!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in o&&(o.length===0||r.node(o[0]))},r.string=function(o){return typeof o=="string"||o instanceof String},r.fn=function(o){var s=Object.prototype.toString.call(o);return s==="[object Function]"}}),370:(function(i,r,o){var s=o(879),a=o(438);function l(p,y,m){if(!p&&!y&&!m)throw new Error("Missing required arguments");if(!s.string(y))throw new TypeError("Second argument must be a String");if(!s.fn(m))throw new TypeError("Third argument must be a Function");if(s.node(p))return c(p,y,m);if(s.nodeList(p))return u(p,y,m);if(s.string(p))return d(p,y,m);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(p,y,m){return p.addEventListener(y,m),{destroy:function(){p.removeEventListener(y,m)}}}function u(p,y,m){return Array.prototype.forEach.call(p,function(g){g.addEventListener(y,m)}),{destroy:function(){Array.prototype.forEach.call(p,function(g){g.removeEventListener(y,m)})}}}function d(p,y,m){return a(document.body,p,y,m)}i.exports=l}),817:(function(i){function r(o){var s;if(o.nodeName==="SELECT")o.focus(),s=o.value;else if(o.nodeName==="INPUT"||o.nodeName==="TEXTAREA"){var a=o.hasAttribute("readonly");a||o.setAttribute("readonly",""),o.select(),o.setSelectionRange(0,o.value.length),a||o.removeAttribute("readonly"),s=o.value}else{o.hasAttribute("contenteditable")&&o.focus();var l=window.getSelection(),c=document.createRange();c.selectNodeContents(o),l.removeAllRanges(),l.addRange(c),s=l.toString()}return s}i.exports=r}),279:(function(i){function r(){}r.prototype={on:function(o,s,a){var l=this.e||(this.e={});return(l[o]||(l[o]=[])).push({fn:s,ctx:a}),this},once:function(o,s,a){var l=this;function c(){l.off(o,c),s.apply(a,arguments)}return c._=s,this.on(o,c,a)},emit:function(o){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[o]||[]).slice(),l=0,c=a.length;for(l;lSs,afterRead:()=>xs,afterWrite:()=>Os,applyStyles:()=>pn,arrow:()=>Ni,auto:()=>zn,basePlacements:()=>lt,beforeMain:()=>Ts,beforeRead:()=>_s,beforeWrite:()=>As,bottom:()=>ge,clippingParents:()=>Yr,computeStyles:()=>gn,createPopper:()=>Qn,createPopperBase:()=>Fs,createPopperLite:()=>$s,detectOverflow:()=>ke,end:()=>_t,eventListeners:()=>vn,flip:()=>Ri,hide:()=>Pi,left:()=>pe,main:()=>Cs,modifierPhases:()=>Kr,offset:()=>Fi,placements:()=>jn,popper:()=>Bt,popperGenerator:()=>Gt,popperOffsets:()=>bn,preventOverflow:()=>$i,read:()=>ws,reference:()=>Gr,right:()=>me,start:()=>rt,top:()=>de,variationPlacements:()=>Mi,viewport:()=>qn,write:()=>Ds});var de="top",ge="bottom",me="right",pe="left",zn="auto",lt=[de,ge,me,pe],rt="start",_t="end",Yr="clippingParents",qn="viewport",Bt="popper",Gr="reference",Mi=lt.reduce(function(n,e){return n.concat([e+"-"+rt,e+"-"+_t])},[]),jn=[].concat(lt,[zn]).reduce(function(n,e){return n.concat([e,e+"-"+rt,e+"-"+_t])},[]),_s="beforeRead",ws="read",xs="afterRead",Ts="beforeMain",Cs="main",Ss="afterMain",As="beforeWrite",Ds="write",Os="afterWrite",Kr=[_s,ws,xs,Ts,Cs,Ss,As,Ds,Os];function xe(n){return n?(n.nodeName||"").toLowerCase():null}function ce(n){if(n==null)return window;if(n.toString()!=="[object Window]"){var e=n.ownerDocument;return e&&e.defaultView||window}return n}function Ye(n){var e=ce(n).Element;return n instanceof e||n instanceof Element}function _e(n){var e=ce(n).HTMLElement;return n instanceof e||n instanceof HTMLElement}function hn(n){if(typeof ShadowRoot=="undefined")return!1;var e=ce(n).ShadowRoot;return n instanceof e||n instanceof ShadowRoot}function xu(n){var e=n.state;Object.keys(e.elements).forEach(function(t){var i=e.styles[t]||{},r=e.attributes[t]||{},o=e.elements[t];!_e(o)||!xe(o)||(Object.assign(o.style,i),Object.keys(r).forEach(function(s){var a=r[s];a===!1?o.removeAttribute(s):o.setAttribute(s,a===!0?"":a)}))})}function Tu(n){var e=n.state,t={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,t.popper),e.styles=t,e.elements.arrow&&Object.assign(e.elements.arrow.style,t.arrow),function(){Object.keys(e.elements).forEach(function(i){var r=e.elements[i],o=e.attributes[i]||{},s=Object.keys(e.styles.hasOwnProperty(i)?e.styles[i]:t[i]),a=s.reduce(function(l,c){return l[c]="",l},{});!_e(r)||!xe(r)||(Object.assign(r.style,a),Object.keys(o).forEach(function(l){r.removeAttribute(l)}))})}}var pn={name:"applyStyles",enabled:!0,phase:"write",fn:xu,effect:Tu,requires:["computeStyles"]};function Te(n){return n.split("-")[0]}var Ze=Math.max,Vt=Math.min,ct=Math.round;function mn(){var n=navigator.userAgentData;return n!=null&&n.brands&&Array.isArray(n.brands)?n.brands.map(function(e){return e.brand+"/"+e.version}).join(" "):navigator.userAgent}function Wn(){return!/^((?!chrome|android).)*safari/i.test(mn())}function Ge(n,e,t){e===void 0&&(e=!1),t===void 0&&(t=!1);var i=n.getBoundingClientRect(),r=1,o=1;e&&_e(n)&&(r=n.offsetWidth>0&&ct(i.width)/n.offsetWidth||1,o=n.offsetHeight>0&&ct(i.height)/n.offsetHeight||1);var s=Ye(n)?ce(n):window,a=s.visualViewport,l=!Wn()&&t,c=(i.left+(l&&a?a.offsetLeft:0))/r,u=(i.top+(l&&a?a.offsetTop:0))/o,d=i.width/r,p=i.height/o;return{width:d,height:p,top:u,right:c+d,bottom:u+p,left:c,x:c,y:u}}function zt(n){var e=Ge(n),t=n.offsetWidth,i=n.offsetHeight;return Math.abs(e.width-t)<=1&&(t=e.width),Math.abs(e.height-i)<=1&&(i=e.height),{x:n.offsetLeft,y:n.offsetTop,width:t,height:i}}function Un(n,e){var t=e.getRootNode&&e.getRootNode();if(n.contains(e))return!0;if(t&&hn(t)){var i=e;do{if(i&&n.isSameNode(i))return!0;i=i.parentNode||i.host}while(i)}return!1}function Ne(n){return ce(n).getComputedStyle(n)}function Xr(n){return["table","td","th"].indexOf(xe(n))>=0}function Se(n){return((Ye(n)?n.ownerDocument:n.document)||window.document).documentElement}function ut(n){return xe(n)==="html"?n:n.assignedSlot||n.parentNode||(hn(n)?n.host:null)||Se(n)}function Ls(n){return!_e(n)||Ne(n).position==="fixed"?null:n.offsetParent}function Cu(n){var e=/firefox/i.test(mn()),t=/Trident/i.test(mn());if(t&&_e(n)){var i=Ne(n);if(i.position==="fixed")return null}var r=ut(n);for(hn(r)&&(r=r.host);_e(r)&&["html","body"].indexOf(xe(r))<0;){var o=Ne(r);if(o.transform!=="none"||o.perspective!=="none"||o.contain==="paint"||["transform","perspective"].indexOf(o.willChange)!==-1||e&&o.willChange==="filter"||e&&o.filter&&o.filter!=="none")return r;r=r.parentNode}return null}function et(n){for(var e=ce(n),t=Ls(n);t&&Xr(t)&&Ne(t).position==="static";)t=Ls(t);return t&&(xe(t)==="html"||xe(t)==="body"&&Ne(t).position==="static")?e:t||Cu(n)||e}function qt(n){return["top","bottom"].indexOf(n)>=0?"x":"y"}function jt(n,e,t){return Ze(n,Vt(e,t))}function Ms(n,e,t){var i=jt(n,e,t);return i>t?t:i}function Yn(){return{top:0,right:0,bottom:0,left:0}}function Gn(n){return Object.assign({},Yn(),n)}function Kn(n,e){return e.reduce(function(t,i){return t[i]=n,t},{})}var Su=function(e,t){return e=typeof e=="function"?e(Object.assign({},t.rects,{placement:t.placement})):e,Gn(typeof e!="number"?e:Kn(e,lt))};function Au(n){var e,t=n.state,i=n.name,r=n.options,o=t.elements.arrow,s=t.modifiersData.popperOffsets,a=Te(t.placement),l=qt(a),c=[pe,me].indexOf(a)>=0,u=c?"height":"width";if(!(!o||!s)){var d=Su(r.padding,t),p=zt(o),y=l==="y"?de:pe,m=l==="y"?ge:me,g=t.rects.reference[u]+t.rects.reference[l]-s[l]-t.rects.popper[u],_=s[l]-t.rects.reference[l],x=et(o),A=x?l==="y"?x.clientHeight||0:x.clientWidth||0:0,w=g/2-_/2,C=d[y],$=A-p[u]-d[m],q=A/2-p[u]/2+w,H=jt(C,q,$),I=l;t.modifiersData[i]=(e={},e[I]=H,e.centerOffset=H-q,e)}}function Du(n){var e=n.state,t=n.options,i=t.element,r=i===void 0?"[data-popper-arrow]":i;r!=null&&(typeof r=="string"&&(r=e.elements.popper.querySelector(r),!r)||Un(e.elements.popper,r)&&(e.elements.arrow=r))}var Ni={name:"arrow",enabled:!0,phase:"main",fn:Au,effect:Du,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Ke(n){return n.split("-")[1]}var Ou={top:"auto",right:"auto",bottom:"auto",left:"auto"};function Lu(n,e){var t=n.x,i=n.y,r=e.devicePixelRatio||1;return{x:ct(t*r)/r||0,y:ct(i*r)/r||0}}function Ns(n){var e,t=n.popper,i=n.popperRect,r=n.placement,o=n.variation,s=n.offsets,a=n.position,l=n.gpuAcceleration,c=n.adaptive,u=n.roundOffsets,d=n.isFixed,p=s.x,y=p===void 0?0:p,m=s.y,g=m===void 0?0:m,_=typeof u=="function"?u({x:y,y:g}):{x:y,y:g};y=_.x,g=_.y;var x=s.hasOwnProperty("x"),A=s.hasOwnProperty("y"),w=pe,C=de,$=window;if(c){var q=et(t),H="clientHeight",I="clientWidth";if(q===ce(t)&&(q=Se(t),Ne(q).position!=="static"&&a==="absolute"&&(H="scrollHeight",I="scrollWidth")),q=q,r===de||(r===pe||r===me)&&o===_t){C=ge;var L=d&&q===$&&$.visualViewport?$.visualViewport.height:q[H];g-=L-i.height,g*=l?1:-1}if(r===pe||(r===de||r===ge)&&o===_t){w=me;var j=d&&q===$&&$.visualViewport?$.visualViewport.width:q[I];y-=j-i.width,y*=l?1:-1}}var G=Object.assign({position:a},c&&Ou),Q=u===!0?Lu({x:y,y:g},ce(t)):{x:y,y:g};if(y=Q.x,g=Q.y,l){var Z;return Object.assign({},G,(Z={},Z[C]=A?"0":"",Z[w]=x?"0":"",Z.transform=($.devicePixelRatio||1)<=1?"translate("+y+"px, "+g+"px)":"translate3d("+y+"px, "+g+"px, 0)",Z))}return Object.assign({},G,(e={},e[C]=A?g+"px":"",e[w]=x?y+"px":"",e.transform="",e))}function Mu(n){var e=n.state,t=n.options,i=t.gpuAcceleration,r=i===void 0?!0:i,o=t.adaptive,s=o===void 0?!0:o,a=t.roundOffsets,l=a===void 0?!0:a,c={placement:Te(e.placement),variation:Ke(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:r,isFixed:e.options.strategy==="fixed"};e.modifiersData.popperOffsets!=null&&(e.styles.popper=Object.assign({},e.styles.popper,Ns(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:s,roundOffsets:l})))),e.modifiersData.arrow!=null&&(e.styles.arrow=Object.assign({},e.styles.arrow,Ns(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})}var gn={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:Mu,data:{}};var ki={passive:!0};function Nu(n){var e=n.state,t=n.instance,i=n.options,r=i.scroll,o=r===void 0?!0:r,s=i.resize,a=s===void 0?!0:s,l=ce(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach(function(u){u.addEventListener("scroll",t.update,ki)}),a&&l.addEventListener("resize",t.update,ki),function(){o&&c.forEach(function(u){u.removeEventListener("scroll",t.update,ki)}),a&&l.removeEventListener("resize",t.update,ki)}}var vn={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:Nu,data:{}};var ku={left:"right",right:"left",bottom:"top",top:"bottom"};function yn(n){return n.replace(/left|right|bottom|top/g,function(e){return ku[e]})}var Hu={start:"end",end:"start"};function Hi(n){return n.replace(/start|end/g,function(e){return Hu[e]})}function Wt(n){var e=ce(n),t=e.pageXOffset,i=e.pageYOffset;return{scrollLeft:t,scrollTop:i}}function Ut(n){return Ge(Se(n)).left+Wt(n).scrollLeft}function Qr(n,e){var t=ce(n),i=Se(n),r=t.visualViewport,o=i.clientWidth,s=i.clientHeight,a=0,l=0;if(r){o=r.width,s=r.height;var c=Wn();(c||!c&&e==="fixed")&&(a=r.offsetLeft,l=r.offsetTop)}return{width:o,height:s,x:a+Ut(n),y:l}}function Jr(n){var e,t=Se(n),i=Wt(n),r=(e=n.ownerDocument)==null?void 0:e.body,o=Ze(t.scrollWidth,t.clientWidth,r?r.scrollWidth:0,r?r.clientWidth:0),s=Ze(t.scrollHeight,t.clientHeight,r?r.scrollHeight:0,r?r.clientHeight:0),a=-i.scrollLeft+Ut(n),l=-i.scrollTop;return Ne(r||t).direction==="rtl"&&(a+=Ze(t.clientWidth,r?r.clientWidth:0)-o),{width:o,height:s,x:a,y:l}}function Yt(n){var e=Ne(n),t=e.overflow,i=e.overflowX,r=e.overflowY;return/auto|scroll|overlay|hidden/.test(t+r+i)}function Ii(n){return["html","body","#document"].indexOf(xe(n))>=0?n.ownerDocument.body:_e(n)&&Yt(n)?n:Ii(ut(n))}function wt(n,e){var t;e===void 0&&(e=[]);var i=Ii(n),r=i===((t=n.ownerDocument)==null?void 0:t.body),o=ce(i),s=r?[o].concat(o.visualViewport||[],Yt(i)?i:[]):i,a=e.concat(s);return r?a:a.concat(wt(ut(s)))}function En(n){return Object.assign({},n,{left:n.x,top:n.y,right:n.x+n.width,bottom:n.y+n.height})}function Iu(n,e){var t=Ge(n,!1,e==="fixed");return t.top=t.top+n.clientTop,t.left=t.left+n.clientLeft,t.bottom=t.top+n.clientHeight,t.right=t.left+n.clientWidth,t.width=n.clientWidth,t.height=n.clientHeight,t.x=t.left,t.y=t.top,t}function ks(n,e,t){return e===qn?En(Qr(n,t)):Ye(e)?Iu(e,t):En(Jr(Se(n)))}function Ru(n){var e=wt(ut(n)),t=["absolute","fixed"].indexOf(Ne(n).position)>=0,i=t&&_e(n)?et(n):n;return Ye(i)?e.filter(function(r){return Ye(r)&&Un(r,i)&&xe(r)!=="body"}):[]}function Zr(n,e,t,i){var r=e==="clippingParents"?Ru(n):[].concat(e),o=[].concat(r,[t]),s=o[0],a=o.reduce(function(l,c){var u=ks(n,c,i);return l.top=Ze(u.top,l.top),l.right=Vt(u.right,l.right),l.bottom=Vt(u.bottom,l.bottom),l.left=Ze(u.left,l.left),l},ks(n,s,i));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}function Xn(n){var e=n.reference,t=n.element,i=n.placement,r=i?Te(i):null,o=i?Ke(i):null,s=e.x+e.width/2-t.width/2,a=e.y+e.height/2-t.height/2,l;switch(r){case de:l={x:s,y:e.y-t.height};break;case ge:l={x:s,y:e.y+e.height};break;case me:l={x:e.x+e.width,y:a};break;case pe:l={x:e.x-t.width,y:a};break;default:l={x:e.x,y:e.y}}var c=r?qt(r):null;if(c!=null){var u=c==="y"?"height":"width";switch(o){case rt:l[c]=l[c]-(e[u]/2-t[u]/2);break;case _t:l[c]=l[c]+(e[u]/2-t[u]/2);break;default:}}return l}function ke(n,e){e===void 0&&(e={});var t=e,i=t.placement,r=i===void 0?n.placement:i,o=t.strategy,s=o===void 0?n.strategy:o,a=t.boundary,l=a===void 0?Yr:a,c=t.rootBoundary,u=c===void 0?qn:c,d=t.elementContext,p=d===void 0?Bt:d,y=t.altBoundary,m=y===void 0?!1:y,g=t.padding,_=g===void 0?0:g,x=Gn(typeof _!="number"?_:Kn(_,lt)),A=p===Bt?Gr:Bt,w=n.rects.popper,C=n.elements[m?A:p],$=Zr(Ye(C)?C:C.contextElement||Se(n.elements.popper),l,u,s),q=Ge(n.elements.reference),H=Xn({reference:q,element:w,strategy:"absolute",placement:r}),I=En(Object.assign({},w,H)),L=p===Bt?I:q,j={top:$.top-L.top+x.top,bottom:L.bottom-$.bottom+x.bottom,left:$.left-L.left+x.left,right:L.right-$.right+x.right},G=n.modifiersData.offset;if(p===Bt&&G){var Q=G[r];Object.keys(j).forEach(function(Z){var he=[me,ge].indexOf(Z)>=0?1:-1,Ce=[de,ge].indexOf(Z)>=0?"y":"x";j[Z]+=Q[Ce]*he})}return j}function eo(n,e){e===void 0&&(e={});var t=e,i=t.placement,r=t.boundary,o=t.rootBoundary,s=t.padding,a=t.flipVariations,l=t.allowedAutoPlacements,c=l===void 0?jn:l,u=Ke(i),d=u?a?Mi:Mi.filter(function(m){return Ke(m)===u}):lt,p=d.filter(function(m){return c.indexOf(m)>=0});p.length===0&&(p=d);var y=p.reduce(function(m,g){return m[g]=ke(n,{placement:g,boundary:r,rootBoundary:o,padding:s})[Te(g)],m},{});return Object.keys(y).sort(function(m,g){return y[m]-y[g]})}function Pu(n){if(Te(n)===zn)return[];var e=yn(n);return[Hi(n),e,Hi(e)]}function Fu(n){var e=n.state,t=n.options,i=n.name;if(!e.modifiersData[i]._skip){for(var r=t.mainAxis,o=r===void 0?!0:r,s=t.altAxis,a=s===void 0?!0:s,l=t.fallbackPlacements,c=t.padding,u=t.boundary,d=t.rootBoundary,p=t.altBoundary,y=t.flipVariations,m=y===void 0?!0:y,g=t.allowedAutoPlacements,_=e.options.placement,x=Te(_),A=x===_,w=l||(A||!m?[yn(_)]:Pu(_)),C=[_].concat(w).reduce(function(V,U){return V.concat(Te(U)===zn?eo(e,{placement:U,boundary:u,rootBoundary:d,padding:c,flipVariations:m,allowedAutoPlacements:g}):U)},[]),$=e.rects.reference,q=e.rects.popper,H=new Map,I=!0,L=C[0],j=0;j=0,Ce=he?"width":"height",ie=ke(e,{placement:G,boundary:u,rootBoundary:d,altBoundary:p,padding:c}),ne=he?Z?me:pe:Z?ge:de;$[Ce]>q[Ce]&&(ne=yn(ne));var Ue=yn(ne),Re=[];if(o&&Re.push(ie[Q]<=0),a&&Re.push(ie[ne]<=0,ie[Ue]<=0),Re.every(function(V){return V})){L=G,I=!1;break}H.set(G,Re)}if(I)for(var W=m?3:1,M=function(U){var Y=C.find(function(ee){var ae=H.get(ee);if(ae)return ae.slice(0,U).every(function(bt){return bt})});if(Y)return L=Y,"break"},D=W;D>0;D--){var B=M(D);if(B==="break")break}e.placement!==L&&(e.modifiersData[i]._skip=!0,e.placement=L,e.reset=!0)}}var Ri={name:"flip",enabled:!0,phase:"main",fn:Fu,requiresIfExists:["offset"],data:{_skip:!1}};function Hs(n,e,t){return t===void 0&&(t={x:0,y:0}),{top:n.top-e.height-t.y,right:n.right-e.width+t.x,bottom:n.bottom-e.height+t.y,left:n.left-e.width-t.x}}function Is(n){return[de,me,ge,pe].some(function(e){return n[e]>=0})}function $u(n){var e=n.state,t=n.name,i=e.rects.reference,r=e.rects.popper,o=e.modifiersData.preventOverflow,s=ke(e,{elementContext:"reference"}),a=ke(e,{altBoundary:!0}),l=Hs(s,i),c=Hs(a,r,o),u=Is(l),d=Is(c);e.modifiersData[t]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:u,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":u,"data-popper-escaped":d})}var Pi={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:$u};function Bu(n,e,t){var i=Te(n),r=[pe,de].indexOf(i)>=0?-1:1,o=typeof t=="function"?t(Object.assign({},e,{placement:n})):t,s=o[0],a=o[1];return s=s||0,a=(a||0)*r,[pe,me].indexOf(i)>=0?{x:a,y:s}:{x:s,y:a}}function Vu(n){var e=n.state,t=n.options,i=n.name,r=t.offset,o=r===void 0?[0,0]:r,s=jn.reduce(function(u,d){return u[d]=Bu(d,e.rects,o),u},{}),a=s[e.placement],l=a.x,c=a.y;e.modifiersData.popperOffsets!=null&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[i]=s}var Fi={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:Vu};function zu(n){var e=n.state,t=n.name;e.modifiersData[t]=Xn({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})}var bn={name:"popperOffsets",enabled:!0,phase:"read",fn:zu,data:{}};function to(n){return n==="x"?"y":"x"}function qu(n){var e=n.state,t=n.options,i=n.name,r=t.mainAxis,o=r===void 0?!0:r,s=t.altAxis,a=s===void 0?!1:s,l=t.boundary,c=t.rootBoundary,u=t.altBoundary,d=t.padding,p=t.tether,y=p===void 0?!0:p,m=t.tetherOffset,g=m===void 0?0:m,_=ke(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:u}),x=Te(e.placement),A=Ke(e.placement),w=!A,C=qt(x),$=to(C),q=e.modifiersData.popperOffsets,H=e.rects.reference,I=e.rects.popper,L=typeof g=="function"?g(Object.assign({},e.rects,{placement:e.placement})):g,j=typeof L=="number"?{mainAxis:L,altAxis:L}:Object.assign({mainAxis:0,altAxis:0},L),G=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,Q={x:0,y:0};if(q){if(o){var Z,he=C==="y"?de:pe,Ce=C==="y"?ge:me,ie=C==="y"?"height":"width",ne=q[C],Ue=ne+_[he],Re=ne-_[Ce],W=y?-I[ie]/2:0,M=A===rt?H[ie]:I[ie],D=A===rt?-I[ie]:-H[ie],B=e.elements.arrow,V=y&&B?zt(B):{width:0,height:0},U=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:Yn(),Y=U[he],ee=U[Ce],ae=jt(0,H[ie],V[ie]),bt=w?H[ie]/2-W-ae-Y-j.mainAxis:M-ae-Y-j.mainAxis,Br=w?-H[ie]/2+W+ae+ee+j.mainAxis:D+ae+ee+j.mainAxis,sn=e.elements.arrow&&et(e.elements.arrow),an=sn?C==="y"?sn.clientTop||0:sn.clientLeft||0:0,xi=(Z=G==null?void 0:G[C])!=null?Z:0,Vr=ne+bt-xi-an,Ti=ne+Br-xi,Ci=jt(y?Vt(Ue,Vr):Ue,ne,y?Ze(Re,Ti):Re);q[C]=Ci,Q[C]=Ci-ne}if(a){var Fn,Si=C==="x"?de:pe,ln=C==="x"?ge:me,ot=q[$],cn=$==="y"?"height":"width",$n=ot+_[Si],un=ot-_[ln],dn=[de,pe].indexOf(x)!==-1,$t=(Fn=G==null?void 0:G[$])!=null?Fn:0,Ai=dn?$n:ot-H[cn]-I[cn]-$t+j.altAxis,Bn=dn?ot+H[cn]+I[cn]-$t-j.altAxis:un,Di=y&&dn?Ms(Ai,ot,Bn):jt(y?Ai:$n,ot,y?Bn:un);q[$]=Di,Q[$]=Di-ot}e.modifiersData[i]=Q}}var $i={name:"preventOverflow",enabled:!0,phase:"main",fn:qu,requiresIfExists:["offset"]};function no(n){return{scrollLeft:n.scrollLeft,scrollTop:n.scrollTop}}function io(n){return n===ce(n)||!_e(n)?Wt(n):no(n)}function ju(n){var e=n.getBoundingClientRect(),t=ct(e.width)/n.offsetWidth||1,i=ct(e.height)/n.offsetHeight||1;return t!==1||i!==1}function ro(n,e,t){t===void 0&&(t=!1);var i=_e(e),r=_e(e)&&ju(e),o=Se(e),s=Ge(n,r,t),a={scrollLeft:0,scrollTop:0},l={x:0,y:0};return(i||!i&&!t)&&((xe(e)!=="body"||Yt(o))&&(a=io(e)),_e(e)?(l=Ge(e,!0),l.x+=e.clientLeft,l.y+=e.clientTop):o&&(l.x=Ut(o))),{x:s.left+a.scrollLeft-l.x,y:s.top+a.scrollTop-l.y,width:s.width,height:s.height}}function Wu(n){var e=new Map,t=new Set,i=[];n.forEach(function(o){e.set(o.name,o)});function r(o){t.add(o.name);var s=[].concat(o.requires||[],o.requiresIfExists||[]);s.forEach(function(a){if(!t.has(a)){var l=e.get(a);l&&r(l)}}),i.push(o)}return n.forEach(function(o){t.has(o.name)||r(o)}),i}function oo(n){var e=Wu(n);return Kr.reduce(function(t,i){return t.concat(e.filter(function(r){return r.phase===i}))},[])}function so(n){var e;return function(){return e||(e=new Promise(function(t){Promise.resolve().then(function(){e=void 0,t(n())})})),e}}function ao(n){var e=n.reduce(function(t,i){var r=t[i.name];return t[i.name]=r?Object.assign({},r,i,{options:Object.assign({},r.options,i.options),data:Object.assign({},r.data,i.data)}):i,t},{});return Object.keys(e).map(function(t){return e[t]})}var Rs={placement:"bottom",modifiers:[],strategy:"absolute"};function Ps(){for(var n=arguments.length,e=new Array(n),t=0;t(n&&window.CSS&&window.CSS.escape&&(n=n.replace(/#([^\s"#']+)/g,(e,t)=>`#${CSS.escape(t)}`)),n),Xu=n=>n==null?`${n}`:Object.prototype.toString.call(n).match(/\s([a-z]+)/i)[1].toLowerCase(),Qu=n=>{do n+=Math.floor(Math.random()*Gu);while(document.getElementById(n));return n},Ju=n=>{if(!n)return 0;let{transitionDuration:e,transitionDelay:t}=window.getComputedStyle(n),i=Number.parseFloat(e),r=Number.parseFloat(t);return!i&&!r?0:(e=e.split(",")[0],t=t.split(",")[0],(Number.parseFloat(e)+Number.parseFloat(t))*Ku)},ma=n=>{n.dispatchEvent(new Event(Co))},dt=n=>!n||typeof n!="object"?!1:(typeof n.jquery!="undefined"&&(n=n[0]),typeof n.nodeType!="undefined"),Tt=n=>dt(n)?n.jquery?n[0]:n:typeof n=="string"&&n.length>0?document.querySelector(pa(n)):null,An=n=>{if(!dt(n)||n.getClientRects().length===0)return!1;let e=getComputedStyle(n).getPropertyValue("visibility")==="visible",t=n.closest("details:not([open])");if(!t)return e;if(t!==n){let i=n.closest("summary");if(i&&i.parentNode!==t||i===null)return!1}return e},Ct=n=>!n||n.nodeType!==Node.ELEMENT_NODE||n.classList.contains("disabled")?!0:typeof n.disabled!="undefined"?n.disabled:n.hasAttribute("disabled")&&n.getAttribute("disabled")!=="false",ga=n=>{if(!document.documentElement.attachShadow)return null;if(typeof n.getRootNode=="function"){let e=n.getRootNode();return e instanceof ShadowRoot?e:null}return n instanceof ShadowRoot?n:n.parentNode?ga(n.parentNode):null},Ki=()=>{},ni=n=>{n.offsetHeight},va=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,co=[],Zu=n=>{document.readyState==="loading"?(co.length||document.addEventListener("DOMContentLoaded",()=>{for(let e of co)e()}),co.push(n)):n()},Xe=()=>document.documentElement.dir==="rtl",Je=n=>{Zu(()=>{let e=va();if(e){let t=n.NAME,i=e.fn[t];e.fn[t]=n.jQueryInterface,e.fn[t].Constructor=n,e.fn[t].noConflict=()=>(e.fn[t]=i,n.jQueryInterface)}})},Pe=(n,e=[],t=n)=>typeof n=="function"?n.call(...e):t,ya=(n,e,t=!0)=>{if(!t){Pe(n);return}let r=Ju(e)+5,o=!1,s=({target:a})=>{a===e&&(o=!0,e.removeEventListener(Co,s),Pe(n))};e.addEventListener(Co,s),setTimeout(()=>{o||ma(e)},r)},Oo=(n,e,t,i)=>{let r=n.length,o=n.indexOf(e);return o===-1?!t&&i?n[r-1]:n[0]:(o+=t?1:-1,i&&(o=(o+r)%r),n[Math.max(0,Math.min(o,r-1))])},ed=/[^.]*(?=\..*)\.|.*/,td=/\..*/,nd=/::\d+$/,uo={},Bs=1,Ea={mouseenter:"mouseover",mouseleave:"mouseout"},id=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function ba(n,e){return e&&`${e}::${Bs++}`||n.uidEvent||Bs++}function _a(n){let e=ba(n);return n.uidEvent=e,uo[e]=uo[e]||{},uo[e]}function rd(n,e){return function t(i){return Lo(i,{delegateTarget:n}),t.oneOff&&T.off(n,i.type,e),e.apply(n,[i])}}function od(n,e,t){return function i(r){let o=n.querySelectorAll(e);for(let{target:s}=r;s&&s!==this;s=s.parentNode)for(let a of o)if(a===s)return Lo(r,{delegateTarget:s}),i.oneOff&&T.off(n,r.type,e,t),t.apply(s,[r])}}function wa(n,e,t=null){return Object.values(n).find(i=>i.callable===e&&i.delegationSelector===t)}function xa(n,e,t){let i=typeof e=="string",r=i?t:e||t,o=Ta(n);return id.has(o)||(o=n),[i,r,o]}function Vs(n,e,t,i,r){if(typeof e!="string"||!n)return;let[o,s,a]=xa(e,t,i);e in Ea&&(s=(m=>function(g){if(!g.relatedTarget||g.relatedTarget!==g.delegateTarget&&!g.delegateTarget.contains(g.relatedTarget))return m.call(this,g)})(s));let l=_a(n),c=l[a]||(l[a]={}),u=wa(c,s,o?t:null);if(u){u.oneOff=u.oneOff&&r;return}let d=ba(s,e.replace(ed,"")),p=o?od(n,t,s):rd(n,s);p.delegationSelector=o?t:null,p.callable=s,p.oneOff=r,p.uidEvent=d,c[d]=p,n.addEventListener(a,p,o)}function So(n,e,t,i,r){let o=wa(e[t],i,r);o&&(n.removeEventListener(t,o,!!r),delete e[t][o.uidEvent])}function sd(n,e,t,i){let r=e[t]||{};for(let[o,s]of Object.entries(r))o.includes(i)&&So(n,e,t,s.callable,s.delegationSelector)}function Ta(n){return n=n.replace(td,""),Ea[n]||n}var T={on(n,e,t,i){Vs(n,e,t,i,!1)},one(n,e,t,i){Vs(n,e,t,i,!0)},off(n,e,t,i){if(typeof e!="string"||!n)return;let[r,o,s]=xa(e,t,i),a=s!==e,l=_a(n),c=l[s]||{},u=e.startsWith(".");if(typeof o!="undefined"){if(!Object.keys(c).length)return;So(n,l,s,o,r?t:null);return}if(u)for(let d of Object.keys(l))sd(n,l,d,e.slice(1));for(let[d,p]of Object.entries(c)){let y=d.replace(nd,"");(!a||e.includes(y))&&So(n,l,s,p.callable,p.delegationSelector)}},trigger(n,e,t){if(typeof e!="string"||!n)return null;let i=va(),r=Ta(e),o=e!==r,s=null,a=!0,l=!0,c=!1;o&&i&&(s=i.Event(e,t),i(n).trigger(s),a=!s.isPropagationStopped(),l=!s.isImmediatePropagationStopped(),c=s.isDefaultPrevented());let u=Lo(new Event(e,{bubbles:a,cancelable:!0}),t);return c&&u.preventDefault(),l&&n.dispatchEvent(u),u.defaultPrevented&&s&&s.preventDefault(),u}};function Lo(n,e={}){for(let[t,i]of Object.entries(e))try{n[t]=i}catch(r){Object.defineProperty(n,t,{configurable:!0,get(){return i}})}return n}function zs(n){if(n==="true")return!0;if(n==="false")return!1;if(n===Number(n).toString())return Number(n);if(n===""||n==="null")return null;if(typeof n!="string")return n;try{return JSON.parse(decodeURIComponent(n))}catch(e){return n}}function fo(n){return n.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}var ft={setDataAttribute(n,e,t){n.setAttribute(`data-bs-${fo(e)}`,t)},removeDataAttribute(n,e){n.removeAttribute(`data-bs-${fo(e)}`)},getDataAttributes(n){if(!n)return{};let e={},t=Object.keys(n.dataset).filter(i=>i.startsWith("bs")&&!i.startsWith("bsConfig"));for(let i of t){let r=i.replace(/^bs/,"");r=r.charAt(0).toLowerCase()+r.slice(1),e[r]=zs(n.dataset[i])}return e},getDataAttribute(n,e){return zs(n.getAttribute(`data-bs-${fo(e)}`))}},Qt=class{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(e){return e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e}_mergeConfigObj(e,t){let i=dt(t)?ft.getDataAttribute(t,"config"):{};return O(O(O(O({},this.constructor.Default),typeof i=="object"?i:{}),dt(t)?ft.getDataAttributes(t):{}),typeof e=="object"?e:{})}_typeCheckConfig(e,t=this.constructor.DefaultType){for(let[i,r]of Object.entries(t)){let o=e[i],s=dt(o)?"element":Xu(o);if(!new RegExp(r).test(s))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${i}" provided type "${s}" but expected type "${r}".`)}}},ad="5.3.8",We=class extends Qt{constructor(e,t){super(),e=Tt(e),e&&(this._element=e,this._config=this._getConfig(t),lo.set(this._element,this.constructor.DATA_KEY,this))}dispose(){lo.remove(this._element,this.constructor.DATA_KEY),T.off(this._element,this.constructor.EVENT_KEY);for(let e of Object.getOwnPropertyNames(this))this[e]=null}_queueCallback(e,t,i=!0){ya(e,t,i)}_getConfig(e){return e=this._mergeConfigObj(e,this._element),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}static getInstance(e){return lo.get(Tt(e),this.DATA_KEY)}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,typeof t=="object"?t:null)}static get VERSION(){return ad}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(e){return`${e}${this.EVENT_KEY}`}},ho=n=>{let e=n.getAttribute("data-bs-target");if(!e||e==="#"){let t=n.getAttribute("href");if(!t||!t.includes("#")&&!t.startsWith("."))return null;t.includes("#")&&!t.startsWith("#")&&(t=`#${t.split("#")[1]}`),e=t&&t!=="#"?t.trim():null}return e?e.split(",").map(t=>pa(t)).join(","):null},z={find(n,e=document.documentElement){return[].concat(...Element.prototype.querySelectorAll.call(e,n))},findOne(n,e=document.documentElement){return Element.prototype.querySelector.call(e,n)},children(n,e){return[].concat(...n.children).filter(t=>t.matches(e))},parents(n,e){let t=[],i=n.parentNode.closest(e);for(;i;)t.push(i),i=i.parentNode.closest(e);return t},prev(n,e){let t=n.previousElementSibling;for(;t;){if(t.matches(e))return[t];t=t.previousElementSibling}return[]},next(n,e){let t=n.nextElementSibling;for(;t;){if(t.matches(e))return[t];t=t.nextElementSibling}return[]},focusableChildren(n){let e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(t=>`${t}:not([tabindex^="-"])`).join(",");return this.find(e,n).filter(t=>!Ct(t)&&An(t))},getSelectorFromElement(n){let e=ho(n);return e&&z.findOne(e)?e:null},getElementFromSelector(n){let e=ho(n);return e?z.findOne(e):null},getMultipleElementsFromSelector(n){let e=ho(n);return e?z.find(e):[]}},ir=(n,e="hide")=>{let t=`click.dismiss${n.EVENT_KEY}`,i=n.NAME;T.on(document,t,`[data-bs-dismiss="${i}"]`,function(r){if(["A","AREA"].includes(this.tagName)&&r.preventDefault(),Ct(this))return;let o=z.getElementFromSelector(this)||this.closest(`.${i}`);n.getOrCreateInstance(o)[e]()})},ld="alert",cd="bs.alert",Ca=`.${cd}`,ud=`close${Ca}`,dd=`closed${Ca}`,fd="fade",hd="show",Xi=class n extends We{static get NAME(){return ld}close(){if(T.trigger(this._element,ud).defaultPrevented)return;this._element.classList.remove(hd);let t=this._element.classList.contains(fd);this._queueCallback(()=>this._destroyElement(),this._element,t)}_destroyElement(){this._element.remove(),T.trigger(this._element,dd),this.dispose()}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)}})}};ir(Xi,"close");Je(Xi);var pd="button",md="bs.button",gd=`.${md}`,vd=".data-api",yd="active",qs='[data-bs-toggle="button"]',Ed=`click${gd}${vd}`,Qi=class n extends We{static get NAME(){return pd}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle(yd))}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this);e==="toggle"&&t[e]()})}};T.on(document,Ed,qs,n=>{n.preventDefault();let e=n.target.closest(qs);Qi.getOrCreateInstance(e).toggle()});Je(Qi);var bd="swipe",Dn=".bs.swipe",_d=`touchstart${Dn}`,wd=`touchmove${Dn}`,xd=`touchend${Dn}`,Td=`pointerdown${Dn}`,Cd=`pointerup${Dn}`,Sd="touch",Ad="pen",Dd="pointer-event",Od=40,Ld={endCallback:null,leftCallback:null,rightCallback:null},Md={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"},Ji=class n extends Qt{constructor(e,t){super(),this._element=e,!(!e||!n.isSupported())&&(this._config=this._getConfig(t),this._deltaX=0,this._supportPointerEvents=!!window.PointerEvent,this._initEvents())}static get Default(){return Ld}static get DefaultType(){return Md}static get NAME(){return bd}dispose(){T.off(this._element,Dn)}_start(e){if(!this._supportPointerEvents){this._deltaX=e.touches[0].clientX;return}this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX)}_end(e){this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX-this._deltaX),this._handleSwipe(),Pe(this._config.endCallback)}_move(e){this._deltaX=e.touches&&e.touches.length>1?0:e.touches[0].clientX-this._deltaX}_handleSwipe(){let e=Math.abs(this._deltaX);if(e<=Od)return;let t=e/this._deltaX;this._deltaX=0,t&&Pe(t>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(T.on(this._element,Td,e=>this._start(e)),T.on(this._element,Cd,e=>this._end(e)),this._element.classList.add(Dd)):(T.on(this._element,_d,e=>this._start(e)),T.on(this._element,wd,e=>this._move(e)),T.on(this._element,xd,e=>this._end(e)))}_eventIsPointerPenTouch(e){return this._supportPointerEvents&&(e.pointerType===Ad||e.pointerType===Sd)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}},Nd="carousel",kd="bs.carousel",Ot=`.${kd}`,Sa=".data-api",Hd="ArrowLeft",Id="ArrowRight",Rd=500,Jn="next",_n="prev",xn="left",Yi="right",Pd=`slide${Ot}`,po=`slid${Ot}`,Fd=`keydown${Ot}`,$d=`mouseenter${Ot}`,Bd=`mouseleave${Ot}`,Vd=`dragstart${Ot}`,zd=`load${Ot}${Sa}`,qd=`click${Ot}${Sa}`,Aa="carousel",Vi="active",jd="slide",Wd="carousel-item-end",Ud="carousel-item-start",Yd="carousel-item-next",Gd="carousel-item-prev",Da=".active",Oa=".carousel-item",Kd=Da+Oa,Xd=".carousel-item img",Qd=".carousel-indicators",Jd="[data-bs-slide], [data-bs-slide-to]",Zd='[data-bs-ride="carousel"]',ef={[Hd]:Yi,[Id]:xn},tf={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},nf={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"},ei=class n extends We{constructor(e,t){super(e,t),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(Qd,this._element),this._addEventListeners(),this._config.ride===Aa&&this.cycle()}static get Default(){return tf}static get DefaultType(){return nf}static get NAME(){return Nd}next(){this._slide(Jn)}nextWhenVisible(){!document.hidden&&An(this._element)&&this.next()}prev(){this._slide(_n)}pause(){this._isSliding&&ma(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){if(this._config.ride){if(this._isSliding){T.one(this._element,po,()=>this.cycle());return}this.cycle()}}to(e){let t=this._getItems();if(e>t.length-1||e<0)return;if(this._isSliding){T.one(this._element,po,()=>this.to(e));return}let i=this._getItemIndex(this._getActive());if(i===e)return;let r=e>i?Jn:_n;this._slide(r,t[e])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(e){return e.defaultInterval=e.interval,e}_addEventListeners(){this._config.keyboard&&T.on(this._element,Fd,e=>this._keydown(e)),this._config.pause==="hover"&&(T.on(this._element,$d,()=>this.pause()),T.on(this._element,Bd,()=>this._maybeEnableCycle())),this._config.touch&&Ji.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(let i of z.find(Xd,this._element))T.on(i,Vd,r=>r.preventDefault());let t={leftCallback:()=>this._slide(this._directionToOrder(xn)),rightCallback:()=>this._slide(this._directionToOrder(Yi)),endCallback:()=>{this._config.pause==="hover"&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),Rd+this._config.interval))}};this._swipeHelper=new Ji(this._element,t)}_keydown(e){if(/input|textarea/i.test(e.target.tagName))return;let t=ef[e.key];t&&(e.preventDefault(),this._slide(this._directionToOrder(t)))}_getItemIndex(e){return this._getItems().indexOf(e)}_setActiveIndicatorElement(e){if(!this._indicatorsElement)return;let t=z.findOne(Da,this._indicatorsElement);t.classList.remove(Vi),t.removeAttribute("aria-current");let i=z.findOne(`[data-bs-slide-to="${e}"]`,this._indicatorsElement);i&&(i.classList.add(Vi),i.setAttribute("aria-current","true"))}_updateInterval(){let e=this._activeElement||this._getActive();if(!e)return;let t=Number.parseInt(e.getAttribute("data-bs-interval"),10);this._config.interval=t||this._config.defaultInterval}_slide(e,t=null){if(this._isSliding)return;let i=this._getActive(),r=e===Jn,o=t||Oo(this._getItems(),i,r,this._config.wrap);if(o===i)return;let s=this._getItemIndex(o),a=y=>T.trigger(this._element,y,{relatedTarget:o,direction:this._orderToDirection(e),from:this._getItemIndex(i),to:s});if(a(Pd).defaultPrevented||!i||!o)return;let c=!!this._interval;this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(s),this._activeElement=o;let u=r?Ud:Wd,d=r?Yd:Gd;o.classList.add(d),ni(o),i.classList.add(u),o.classList.add(u);let p=()=>{o.classList.remove(u,d),o.classList.add(Vi),i.classList.remove(Vi,d,u),this._isSliding=!1,a(po)};this._queueCallback(p,i,this._isAnimated()),c&&this.cycle()}_isAnimated(){return this._element.classList.contains(jd)}_getActive(){return z.findOne(Kd,this._element)}_getItems(){return z.find(Oa,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(e){return Xe()?e===xn?_n:Jn:e===xn?Jn:_n}_orderToDirection(e){return Xe()?e===_n?xn:Yi:e===_n?Yi:xn}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="number"){t.to(e);return}if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}};T.on(document,qd,Jd,function(n){let e=z.getElementFromSelector(this);if(!e||!e.classList.contains(Aa))return;n.preventDefault();let t=ei.getOrCreateInstance(e),i=this.getAttribute("data-bs-slide-to");if(i){t.to(i),t._maybeEnableCycle();return}if(ft.getDataAttribute(this,"slide")==="next"){t.next(),t._maybeEnableCycle();return}t.prev(),t._maybeEnableCycle()});T.on(window,zd,()=>{let n=z.find(Zd);for(let e of n)ei.getOrCreateInstance(e)});Je(ei);var rf="collapse",of="bs.collapse",ii=`.${of}`,sf=".data-api",af=`show${ii}`,lf=`shown${ii}`,cf=`hide${ii}`,uf=`hidden${ii}`,df=`click${ii}${sf}`,mo="show",Cn="collapse",zi="collapsing",ff="collapsed",hf=`:scope .${Cn} .${Cn}`,pf="collapse-horizontal",mf="width",gf="height",vf=".collapse.show, .collapse.collapsing",Ao='[data-bs-toggle="collapse"]',yf={parent:null,toggle:!0},Ef={parent:"(null|element)",toggle:"boolean"},St=class n extends We{constructor(e,t){super(e,t),this._isTransitioning=!1,this._triggerArray=[];let i=z.find(Ao);for(let r of i){let o=z.getSelectorFromElement(r),s=z.find(o).filter(a=>a===this._element);o!==null&&s.length&&this._triggerArray.push(r)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return yf}static get DefaultType(){return Ef}static get NAME(){return rf}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let e=[];if(this._config.parent&&(e=this._getFirstLevelChildren(vf).filter(a=>a!==this._element).map(a=>n.getOrCreateInstance(a,{toggle:!1}))),e.length&&e[0]._isTransitioning||T.trigger(this._element,af).defaultPrevented)return;for(let a of e)a.hide();let i=this._getDimension();this._element.classList.remove(Cn),this._element.classList.add(zi),this._element.style[i]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;let r=()=>{this._isTransitioning=!1,this._element.classList.remove(zi),this._element.classList.add(Cn,mo),this._element.style[i]="",T.trigger(this._element,lf)},s=`scroll${i[0].toUpperCase()+i.slice(1)}`;this._queueCallback(r,this._element,!0),this._element.style[i]=`${this._element[s]}px`}hide(){if(this._isTransitioning||!this._isShown()||T.trigger(this._element,cf).defaultPrevented)return;let t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,ni(this._element),this._element.classList.add(zi),this._element.classList.remove(Cn,mo);for(let r of this._triggerArray){let o=z.getElementFromSelector(r);o&&!this._isShown(o)&&this._addAriaAndCollapsedClass([r],!1)}this._isTransitioning=!0;let i=()=>{this._isTransitioning=!1,this._element.classList.remove(zi),this._element.classList.add(Cn),T.trigger(this._element,uf)};this._element.style[t]="",this._queueCallback(i,this._element,!0)}_isShown(e=this._element){return e.classList.contains(mo)}_configAfterMerge(e){return e.toggle=!!e.toggle,e.parent=Tt(e.parent),e}_getDimension(){return this._element.classList.contains(pf)?mf:gf}_initializeChildren(){if(!this._config.parent)return;let e=this._getFirstLevelChildren(Ao);for(let t of e){let i=z.getElementFromSelector(t);i&&this._addAriaAndCollapsedClass([t],this._isShown(i))}}_getFirstLevelChildren(e){let t=z.find(hf,this._config.parent);return z.find(e,this._config.parent).filter(i=>!t.includes(i))}_addAriaAndCollapsedClass(e,t){if(e.length)for(let i of e)i.classList.toggle(ff,!t),i.setAttribute("aria-expanded",t)}static jQueryInterface(e){let t={};return typeof e=="string"&&/show|hide/.test(e)&&(t.toggle=!1),this.each(function(){let i=n.getOrCreateInstance(this,t);if(typeof e=="string"){if(typeof i[e]=="undefined")throw new TypeError(`No method named "${e}"`);i[e]()}})}};T.on(document,df,Ao,function(n){(n.target.tagName==="A"||n.delegateTarget&&n.delegateTarget.tagName==="A")&&n.preventDefault();for(let e of z.getMultipleElementsFromSelector(this))St.getOrCreateInstance(e,{toggle:!1}).toggle()});Je(St);var js="dropdown",bf="bs.dropdown",Zt=`.${bf}`,Mo=".data-api",_f="Escape",Ws="Tab",wf="ArrowUp",Us="ArrowDown",xf=2,Tf=`hide${Zt}`,Cf=`hidden${Zt}`,Sf=`show${Zt}`,Af=`shown${Zt}`,La=`click${Zt}${Mo}`,Ma=`keydown${Zt}${Mo}`,Df=`keyup${Zt}${Mo}`,Tn="show",Of="dropup",Lf="dropend",Mf="dropstart",Nf="dropup-center",kf="dropdown-center",Kt='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',Hf=`${Kt}.${Tn}`,Gi=".dropdown-menu",If=".navbar",Rf=".navbar-nav",Pf=".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",Ff=Xe()?"top-end":"top-start",$f=Xe()?"top-start":"top-end",Bf=Xe()?"bottom-end":"bottom-start",Vf=Xe()?"bottom-start":"bottom-end",zf=Xe()?"left-start":"right-start",qf=Xe()?"right-start":"left-start",jf="top",Wf="bottom",Uf={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Yf={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"},At=class n extends We{constructor(e,t){super(e,t),this._popper=null,this._parent=this._element.parentNode,this._menu=z.next(this._element,Gi)[0]||z.prev(this._element,Gi)[0]||z.findOne(Gi,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return Uf}static get DefaultType(){return Yf}static get NAME(){return js}toggle(){return this._isShown()?this.hide():this.show()}show(){if(Ct(this._element)||this._isShown())return;let e={relatedTarget:this._element};if(!T.trigger(this._element,Sf,e).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(Rf))for(let i of[].concat(...document.body.children))T.on(i,"mouseover",Ki);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Tn),this._element.classList.add(Tn),T.trigger(this._element,Af,e)}}hide(){if(Ct(this._element)||!this._isShown())return;let e={relatedTarget:this._element};this._completeHide(e)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(e){if(!T.trigger(this._element,Tf,e).defaultPrevented){if("ontouchstart"in document.documentElement)for(let i of[].concat(...document.body.children))T.off(i,"mouseover",Ki);this._popper&&this._popper.destroy(),this._menu.classList.remove(Tn),this._element.classList.remove(Tn),this._element.setAttribute("aria-expanded","false"),ft.removeDataAttribute(this._menu,"popper"),T.trigger(this._element,Cf,e)}}_getConfig(e){if(e=super._getConfig(e),typeof e.reference=="object"&&!dt(e.reference)&&typeof e.reference.getBoundingClientRect!="function")throw new TypeError(`${js.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return e}_createPopper(){if(typeof Bi=="undefined")throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org/docs/v2/)");let e=this._element;this._config.reference==="parent"?e=this._parent:dt(this._config.reference)?e=Tt(this._config.reference):typeof this._config.reference=="object"&&(e=this._config.reference);let t=this._getPopperConfig();this._popper=Qn(e,this._menu,t)}_isShown(){return this._menu.classList.contains(Tn)}_getPlacement(){let e=this._parent;if(e.classList.contains(Lf))return zf;if(e.classList.contains(Mf))return qf;if(e.classList.contains(Nf))return jf;if(e.classList.contains(kf))return Wf;let t=getComputedStyle(this._menu).getPropertyValue("--bs-position").trim()==="end";return e.classList.contains(Of)?t?$f:Ff:t?Vf:Bf}_detectNavbar(){return this._element.closest(If)!==null}_getOffset(){let{offset:e}=this._config;return typeof e=="string"?e.split(",").map(t=>Number.parseInt(t,10)):typeof e=="function"?t=>e(t,this._element):e}_getPopperConfig(){let e={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||this._config.display==="static")&&(ft.setDataAttribute(this._menu,"popper","static"),e.modifiers=[{name:"applyStyles",enabled:!1}]),O(O({},e),Pe(this._config.popperConfig,[void 0,e]))}_selectMenuItem({key:e,target:t}){let i=z.find(Pf,this._menu).filter(r=>An(r));i.length&&Oo(i,t,e===Us,!i.includes(t)).focus()}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()}})}static clearMenus(e){if(e.button===xf||e.type==="keyup"&&e.key!==Ws)return;let t=z.find(Hf);for(let i of t){let r=n.getInstance(i);if(!r||r._config.autoClose===!1)continue;let o=e.composedPath(),s=o.includes(r._menu);if(o.includes(r._element)||r._config.autoClose==="inside"&&!s||r._config.autoClose==="outside"&&s||r._menu.contains(e.target)&&(e.type==="keyup"&&e.key===Ws||/input|select|option|textarea|form/i.test(e.target.tagName)))continue;let a={relatedTarget:r._element};e.type==="click"&&(a.clickEvent=e),r._completeHide(a)}}static dataApiKeydownHandler(e){let t=/input|textarea/i.test(e.target.tagName),i=e.key===_f,r=[wf,Us].includes(e.key);if(!r&&!i||t&&!i)return;e.preventDefault();let o=this.matches(Kt)?this:z.prev(this,Kt)[0]||z.next(this,Kt)[0]||z.findOne(Kt,e.delegateTarget.parentNode),s=n.getOrCreateInstance(o);if(r){e.stopPropagation(),s.show(),s._selectMenuItem(e);return}s._isShown()&&(e.stopPropagation(),s.hide(),o.focus())}};T.on(document,Ma,Kt,At.dataApiKeydownHandler);T.on(document,Ma,Gi,At.dataApiKeydownHandler);T.on(document,La,At.clearMenus);T.on(document,Df,At.clearMenus);T.on(document,La,Kt,function(n){n.preventDefault(),At.getOrCreateInstance(this).toggle()});Je(At);var Na="backdrop",Gf="fade",Ys="show",Gs=`mousedown.bs.${Na}`,Kf={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Xf={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"},Zi=class extends Qt{constructor(e){super(),this._config=this._getConfig(e),this._isAppended=!1,this._element=null}static get Default(){return Kf}static get DefaultType(){return Xf}static get NAME(){return Na}show(e){if(!this._config.isVisible){Pe(e);return}this._append();let t=this._getElement();this._config.isAnimated&&ni(t),t.classList.add(Ys),this._emulateAnimation(()=>{Pe(e)})}hide(e){if(!this._config.isVisible){Pe(e);return}this._getElement().classList.remove(Ys),this._emulateAnimation(()=>{this.dispose(),Pe(e)})}dispose(){this._isAppended&&(T.off(this._element,Gs),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){let e=document.createElement("div");e.className=this._config.className,this._config.isAnimated&&e.classList.add(Gf),this._element=e}return this._element}_configAfterMerge(e){return e.rootElement=Tt(e.rootElement),e}_append(){if(this._isAppended)return;let e=this._getElement();this._config.rootElement.append(e),T.on(e,Gs,()=>{Pe(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(e){ya(e,this._getElement(),this._config.isAnimated)}},Qf="focustrap",Jf="bs.focustrap",er=`.${Jf}`,Zf=`focusin${er}`,eh=`keydown.tab${er}`,th="Tab",nh="forward",Ks="backward",ih={autofocus:!0,trapElement:null},rh={autofocus:"boolean",trapElement:"element"},tr=class extends Qt{constructor(e){super(),this._config=this._getConfig(e),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return ih}static get DefaultType(){return rh}static get NAME(){return Qf}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),T.off(document,er),T.on(document,Zf,e=>this._handleFocusin(e)),T.on(document,eh,e=>this._handleKeydown(e)),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,T.off(document,er))}_handleFocusin(e){let{trapElement:t}=this._config;if(e.target===document||e.target===t||t.contains(e.target))return;let i=z.focusableChildren(t);i.length===0?t.focus():this._lastTabNavDirection===Ks?i[i.length-1].focus():i[0].focus()}_handleKeydown(e){e.key===th&&(this._lastTabNavDirection=e.shiftKey?Ks:nh)}},Xs=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Qs=".sticky-top",qi="padding-right",Js="margin-right",ti=class{constructor(){this._element=document.body}getWidth(){let e=document.documentElement.clientWidth;return Math.abs(window.innerWidth-e)}hide(){let e=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,qi,t=>t+e),this._setElementAttributes(Xs,qi,t=>t+e),this._setElementAttributes(Qs,Js,t=>t-e)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,qi),this._resetElementAttributes(Xs,qi),this._resetElementAttributes(Qs,Js)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(e,t,i){let r=this.getWidth(),o=s=>{if(s!==this._element&&window.innerWidth>s.clientWidth+r)return;this._saveInitialAttribute(s,t);let a=window.getComputedStyle(s).getPropertyValue(t);s.style.setProperty(t,`${i(Number.parseFloat(a))}px`)};this._applyManipulationCallback(e,o)}_saveInitialAttribute(e,t){let i=e.style.getPropertyValue(t);i&&ft.setDataAttribute(e,t,i)}_resetElementAttributes(e,t){let i=r=>{let o=ft.getDataAttribute(r,t);if(o===null){r.style.removeProperty(t);return}ft.removeDataAttribute(r,t),r.style.setProperty(t,o)};this._applyManipulationCallback(e,i)}_applyManipulationCallback(e,t){if(dt(e)){t(e);return}for(let i of z.find(e,this._element))t(i)}},oh="modal",sh="bs.modal",Qe=`.${sh}`,ah=".data-api",lh="Escape",ch=`hide${Qe}`,uh=`hidePrevented${Qe}`,ka=`hidden${Qe}`,Ha=`show${Qe}`,dh=`shown${Qe}`,fh=`resize${Qe}`,hh=`click.dismiss${Qe}`,ph=`mousedown.dismiss${Qe}`,mh=`keydown.dismiss${Qe}`,gh=`click${Qe}${ah}`,Zs="modal-open",vh="fade",ea="show",go="modal-static",yh=".modal.show",Eh=".modal-dialog",bh=".modal-body",_h='[data-bs-toggle="modal"]',wh={backdrop:!0,focus:!0,keyboard:!0},xh={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"},tt=class n extends We{constructor(e,t){super(e,t),this._dialog=z.findOne(Eh,this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new ti,this._addEventListeners()}static get Default(){return wh}static get DefaultType(){return xh}static get NAME(){return oh}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){this._isShown||this._isTransitioning||T.trigger(this._element,Ha,{relatedTarget:e}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Zs),this._adjustDialog(),this._backdrop.show(()=>this._showElement(e)))}hide(){!this._isShown||this._isTransitioning||T.trigger(this._element,ch).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(ea),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated()))}dispose(){T.off(window,Qe),T.off(this._dialog,Qe),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Zi({isVisible:!!this._config.backdrop,isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new tr({trapElement:this._element})}_showElement(e){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;let t=z.findOne(bh,this._dialog);t&&(t.scrollTop=0),ni(this._element),this._element.classList.add(ea);let i=()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,T.trigger(this._element,dh,{relatedTarget:e})};this._queueCallback(i,this._dialog,this._isAnimated())}_addEventListeners(){T.on(this._element,mh,e=>{if(e.key===lh){if(this._config.keyboard){this.hide();return}this._triggerBackdropTransition()}}),T.on(window,fh,()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),T.on(this._element,ph,e=>{T.one(this._element,hh,t=>{if(!(this._element!==e.target||this._element!==t.target)){if(this._config.backdrop==="static"){this._triggerBackdropTransition();return}this._config.backdrop&&this.hide()}})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(Zs),this._resetAdjustments(),this._scrollBar.reset(),T.trigger(this._element,ka)})}_isAnimated(){return this._element.classList.contains(vh)}_triggerBackdropTransition(){if(T.trigger(this._element,uh).defaultPrevented)return;let t=this._element.scrollHeight>document.documentElement.clientHeight,i=this._element.style.overflowY;i==="hidden"||this._element.classList.contains(go)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(go),this._queueCallback(()=>{this._element.classList.remove(go),this._queueCallback(()=>{this._element.style.overflowY=i},this._dialog)},this._dialog),this._element.focus())}_adjustDialog(){let e=this._element.scrollHeight>document.documentElement.clientHeight,t=this._scrollBar.getWidth(),i=t>0;if(i&&!e){let r=Xe()?"paddingLeft":"paddingRight";this._element.style[r]=`${t}px`}if(!i&&e){let r=Xe()?"paddingRight":"paddingLeft";this._element.style[r]=`${t}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(e,t){return this.each(function(){let i=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof i[e]=="undefined")throw new TypeError(`No method named "${e}"`);i[e](t)}})}};T.on(document,gh,_h,function(n){let e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&n.preventDefault(),T.one(e,Ha,r=>{r.defaultPrevented||T.one(e,ka,()=>{An(this)&&this.focus()})});let t=z.findOne(yh);t&&tt.getInstance(t).hide(),tt.getOrCreateInstance(e).toggle(this)});ir(tt);Je(tt);var Th="offcanvas",Ch="bs.offcanvas",mt=`.${Ch}`,Ia=".data-api",Sh=`load${mt}${Ia}`,Ah="Escape",ta="show",na="showing",ia="hiding",Dh="offcanvas-backdrop",Ra=".offcanvas.show",Oh=`show${mt}`,Lh=`shown${mt}`,Mh=`hide${mt}`,ra=`hidePrevented${mt}`,Pa=`hidden${mt}`,Nh=`resize${mt}`,kh=`click${mt}${Ia}`,Hh=`keydown.dismiss${mt}`,Ih='[data-bs-toggle="offcanvas"]',Rh={backdrop:!0,keyboard:!0,scroll:!1},Ph={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"},Dt=class n extends We{constructor(e,t){super(e,t),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Rh}static get DefaultType(){return Ph}static get NAME(){return Th}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){if(this._isShown||T.trigger(this._element,Oh,{relatedTarget:e}).defaultPrevented)return;this._isShown=!0,this._backdrop.show(),this._config.scroll||new ti().hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(na);let i=()=>{(!this._config.scroll||this._config.backdrop)&&this._focustrap.activate(),this._element.classList.add(ta),this._element.classList.remove(na),T.trigger(this._element,Lh,{relatedTarget:e})};this._queueCallback(i,this._element,!0)}hide(){if(!this._isShown||T.trigger(this._element,Mh).defaultPrevented)return;this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(ia),this._backdrop.hide();let t=()=>{this._element.classList.remove(ta,ia),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||new ti().reset(),T.trigger(this._element,Pa)};this._queueCallback(t,this._element,!0)}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){let e=()=>{if(this._config.backdrop==="static"){T.trigger(this._element,ra);return}this.hide()},t=!!this._config.backdrop;return new Zi({className:Dh,isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?e:null})}_initializeFocusTrap(){return new tr({trapElement:this._element})}_addEventListeners(){T.on(this._element,Hh,e=>{if(e.key===Ah){if(this._config.keyboard){this.hide();return}T.trigger(this._element,ra)}})}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)}})}};T.on(document,kh,Ih,function(n){let e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),Ct(this))return;T.one(e,Pa,()=>{An(this)&&this.focus()});let t=z.findOne(Ra);t&&t!==e&&Dt.getInstance(t).hide(),Dt.getOrCreateInstance(e).toggle(this)});T.on(window,Sh,()=>{for(let n of z.find(Ra))Dt.getOrCreateInstance(n).show()});T.on(window,Nh,()=>{for(let n of z.find("[aria-modal][class*=show][class*=offcanvas-]"))getComputedStyle(n).position!=="fixed"&&Dt.getOrCreateInstance(n).hide()});ir(Dt);Je(Dt);var Fh=/^aria-[\w-]*$/i,Fa={"*":["class","dir","id","lang","role",Fh],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},$h=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Bh=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Vh=(n,e)=>{let t=n.nodeName.toLowerCase();return e.includes(t)?$h.has(t)?!!Bh.test(n.nodeValue):!0:e.filter(i=>i instanceof RegExp).some(i=>i.test(t))};function zh(n,e,t){if(!n.length)return n;if(t&&typeof t=="function")return t(n);let r=new window.DOMParser().parseFromString(n,"text/html"),o=[].concat(...r.body.querySelectorAll("*"));for(let s of o){let a=s.nodeName.toLowerCase();if(!Object.keys(e).includes(a)){s.remove();continue}let l=[].concat(...s.attributes),c=[].concat(e["*"]||[],e[a]||[]);for(let u of l)Vh(u,c)||s.removeAttribute(u.nodeName)}return r.body.innerHTML}var qh="TemplateFactory",jh={allowList:Fa,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Wh={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Uh={entry:"(string|element|function|null)",selector:"(string|element)"},Do=class extends Qt{constructor(e){super(),this._config=this._getConfig(e)}static get Default(){return jh}static get DefaultType(){return Wh}static get NAME(){return qh}getContent(){return Object.values(this._config.content).map(e=>this._resolvePossibleFunction(e)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(e){return this._checkContent(e),this._config.content=O(O({},this._config.content),e),this}toHtml(){let e=document.createElement("div");e.innerHTML=this._maybeSanitize(this._config.template);for(let[r,o]of Object.entries(this._config.content))this._setContent(e,o,r);let t=e.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&t.classList.add(...i.split(" ")),t}_typeCheckConfig(e){super._typeCheckConfig(e),this._checkContent(e.content)}_checkContent(e){for(let[t,i]of Object.entries(e))super._typeCheckConfig({selector:t,entry:i},Uh)}_setContent(e,t,i){let r=z.findOne(i,e);if(r){if(t=this._resolvePossibleFunction(t),!t){r.remove();return}if(dt(t)){this._putElementInTemplate(Tt(t),r);return}if(this._config.html){r.innerHTML=this._maybeSanitize(t);return}r.textContent=t}}_maybeSanitize(e){return this._config.sanitize?zh(e,this._config.allowList,this._config.sanitizeFn):e}_resolvePossibleFunction(e){return Pe(e,[void 0,this])}_putElementInTemplate(e,t){if(this._config.html){t.innerHTML="",t.append(e);return}t.textContent=e.textContent}},Yh="tooltip",Gh=new Set(["sanitize","allowList","sanitizeFn"]),vo="fade",Kh="modal",ji="show",Xh=".tooltip-inner",oa=`.${Kh}`,sa="hide.bs.modal",Zn="hover",yo="focus",Eo="click",Qh="manual",Jh="hide",Zh="hidden",ep="show",tp="shown",np="inserted",ip="click",rp="focusin",op="focusout",sp="mouseenter",ap="mouseleave",lp={AUTO:"auto",TOP:"top",RIGHT:Xe()?"left":"right",BOTTOM:"bottom",LEFT:Xe()?"right":"left"},cp={allowList:Fa,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},up={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"},ht=class n extends We{constructor(e,t){if(typeof Bi=="undefined")throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org/docs/v2/)");super(e,t),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return cp}static get DefaultType(){return up}static get NAME(){return Yh}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){if(this._isEnabled){if(this._isShown()){this._leave();return}this._enter()}}dispose(){clearTimeout(this._timeout),T.off(this._element.closest(oa),sa,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if(this._element.style.display==="none")throw new Error("Please use show on visible elements");if(!(this._isWithContent()&&this._isEnabled))return;let e=T.trigger(this._element,this.constructor.eventName(ep)),i=(ga(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(e.defaultPrevented||!i)return;this._disposePopper();let r=this._getTipElement();this._element.setAttribute("aria-describedby",r.getAttribute("id"));let{container:o}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(o.append(r),T.trigger(this._element,this.constructor.eventName(np))),this._popper=this._createPopper(r),r.classList.add(ji),"ontouchstart"in document.documentElement)for(let a of[].concat(...document.body.children))T.on(a,"mouseover",Ki);let s=()=>{T.trigger(this._element,this.constructor.eventName(tp)),this._isHovered===!1&&this._leave(),this._isHovered=!1};this._queueCallback(s,this.tip,this._isAnimated())}hide(){if(!this._isShown()||T.trigger(this._element,this.constructor.eventName(Jh)).defaultPrevented)return;if(this._getTipElement().classList.remove(ji),"ontouchstart"in document.documentElement)for(let r of[].concat(...document.body.children))T.off(r,"mouseover",Ki);this._activeTrigger[Eo]=!1,this._activeTrigger[yo]=!1,this._activeTrigger[Zn]=!1,this._isHovered=null;let i=()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),T.trigger(this._element,this.constructor.eventName(Zh)))};this._queueCallback(i,this.tip,this._isAnimated())}update(){this._popper&&this._popper.update()}_isWithContent(){return!!this._getTitle()}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(e){let t=this._getTemplateFactory(e).toHtml();if(!t)return null;t.classList.remove(vo,ji),t.classList.add(`bs-${this.constructor.NAME}-auto`);let i=Qu(this.constructor.NAME).toString();return t.setAttribute("id",i),this._isAnimated()&&t.classList.add(vo),t}setContent(e){this._newContent=e,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(e){return this._templateFactory?this._templateFactory.changeContent(e):this._templateFactory=new Do(oe(O({},this._config),{content:e,extraClass:this._resolvePossibleFunction(this._config.customClass)})),this._templateFactory}_getContentForTemplate(){return{[Xh]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(e){return this.constructor.getOrCreateInstance(e.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(vo)}_isShown(){return this.tip&&this.tip.classList.contains(ji)}_createPopper(e){let t=Pe(this._config.placement,[this,e,this._element]),i=lp[t.toUpperCase()];return Qn(this._element,e,this._getPopperConfig(i))}_getOffset(){let{offset:e}=this._config;return typeof e=="string"?e.split(",").map(t=>Number.parseInt(t,10)):typeof e=="function"?t=>e(t,this._element):e}_resolvePossibleFunction(e){return Pe(e,[this._element,this._element])}_getPopperConfig(e){let t={placement:e,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:i=>{this._getTipElement().setAttribute("data-popper-placement",i.state.placement)}}]};return O(O({},t),Pe(this._config.popperConfig,[void 0,t]))}_setListeners(){let e=this._config.trigger.split(" ");for(let t of e)if(t==="click")T.on(this._element,this.constructor.eventName(ip),this._config.selector,i=>{let r=this._initializeOnDelegatedTarget(i);r._activeTrigger[Eo]=!(r._isShown()&&r._activeTrigger[Eo]),r.toggle()});else if(t!==Qh){let i=t===Zn?this.constructor.eventName(sp):this.constructor.eventName(rp),r=t===Zn?this.constructor.eventName(ap):this.constructor.eventName(op);T.on(this._element,i,this._config.selector,o=>{let s=this._initializeOnDelegatedTarget(o);s._activeTrigger[o.type==="focusin"?yo:Zn]=!0,s._enter()}),T.on(this._element,r,this._config.selector,o=>{let s=this._initializeOnDelegatedTarget(o);s._activeTrigger[o.type==="focusout"?yo:Zn]=s._element.contains(o.relatedTarget),s._leave()})}this._hideModalHandler=()=>{this._element&&this.hide()},T.on(this._element.closest(oa),sa,this._hideModalHandler)}_fixTitle(){let e=this._element.getAttribute("title");e&&(!this._element.getAttribute("aria-label")&&!this._element.textContent.trim()&&this._element.setAttribute("aria-label",e),this._element.setAttribute("data-bs-original-title",e),this._element.removeAttribute("title"))}_enter(){if(this._isShown()||this._isHovered){this._isHovered=!0;return}this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show)}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide))}_setTimeout(e,t){clearTimeout(this._timeout),this._timeout=setTimeout(e,t)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(e){let t=ft.getDataAttributes(this._element);for(let i of Object.keys(t))Gh.has(i)&&delete t[i];return e=O(O({},t),typeof e=="object"&&e?e:{}),e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e.container=e.container===!1?document.body:Tt(e.container),typeof e.delay=="number"&&(e.delay={show:e.delay,hide:e.delay}),typeof e.title=="number"&&(e.title=e.title.toString()),typeof e.content=="number"&&(e.content=e.content.toString()),e}_getDelegateConfig(){let e={};for(let[t,i]of Object.entries(this._config))this.constructor.Default[t]!==i&&(e[t]=i);return e.selector=!1,e.trigger="manual",e}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()}})}};Je(ht);var dp="popover",fp=".popover-header",hp=".popover-body",pp=oe(O({},ht.Default),{content:"",offset:[0,8],placement:"right",template:'',trigger:"click"}),mp=oe(O({},ht.DefaultType),{content:"(null|string|element|function)"}),Sn=class n extends ht{static get Default(){return pp}static get DefaultType(){return mp}static get NAME(){return dp}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[fp]:this._getTitle(),[hp]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()}})}};Je(Sn);var gp="scrollspy",vp="bs.scrollspy",No=`.${vp}`,yp=".data-api",Ep=`activate${No}`,aa=`click${No}`,bp=`load${No}${yp}`,_p="dropdown-item",wn="active",wp='[data-bs-spy="scroll"]',bo="[href]",xp=".nav, .list-group",la=".nav-link",Tp=".nav-item",Cp=".list-group-item",Sp=`${la}, ${Tp} > ${la}, ${Cp}`,Ap=".dropdown",Dp=".dropdown-toggle",Op={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},Lp={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"},nr=class n extends We{constructor(e,t){super(e,t),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement=getComputedStyle(this._element).overflowY==="visible"?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return Op}static get DefaultType(){return Lp}static get NAME(){return gp}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(let e of this._observableSections.values())this._observer.observe(e)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(e){return e.target=Tt(e.target)||document.body,e.rootMargin=e.offset?`${e.offset}px 0px -30%`:e.rootMargin,typeof e.threshold=="string"&&(e.threshold=e.threshold.split(",").map(t=>Number.parseFloat(t))),e}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(T.off(this._config.target,aa),T.on(this._config.target,aa,bo,e=>{let t=this._observableSections.get(e.target.hash);if(t){e.preventDefault();let i=this._rootElement||window,r=t.offsetTop-this._element.offsetTop;if(i.scrollTo){i.scrollTo({top:r,behavior:"smooth"});return}i.scrollTop=r}}))}_getNewObserver(){let e={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver(t=>this._observerCallback(t),e)}_observerCallback(e){let t=s=>this._targetLinks.get(`#${s.target.id}`),i=s=>{this._previousScrollData.visibleEntryTop=s.target.offsetTop,this._process(t(s))},r=(this._rootElement||document.documentElement).scrollTop,o=r>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=r;for(let s of e){if(!s.isIntersecting){this._activeTarget=null,this._clearActiveClass(t(s));continue}let a=s.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(o&&a){if(i(s),!r)return;continue}!o&&!a&&i(s)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;let e=z.find(bo,this._config.target);for(let t of e){if(!t.hash||Ct(t))continue;let i=z.findOne(decodeURI(t.hash),this._element);An(i)&&(this._targetLinks.set(decodeURI(t.hash),t),this._observableSections.set(t.hash,i))}}_process(e){this._activeTarget!==e&&(this._clearActiveClass(this._config.target),this._activeTarget=e,e.classList.add(wn),this._activateParents(e),T.trigger(this._element,Ep,{relatedTarget:e}))}_activateParents(e){if(e.classList.contains(_p)){z.findOne(Dp,e.closest(Ap)).classList.add(wn);return}for(let t of z.parents(e,xp))for(let i of z.prev(t,Sp))i.classList.add(wn)}_clearActiveClass(e){e.classList.remove(wn);let t=z.find(`${bo}.${wn}`,e);for(let i of t)i.classList.remove(wn)}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}};T.on(window,bp,()=>{for(let n of z.find(wp))nr.getOrCreateInstance(n)});Je(nr);var Mp="tab",Np="bs.tab",en=`.${Np}`,kp=`hide${en}`,Hp=`hidden${en}`,Ip=`show${en}`,Rp=`shown${en}`,Pp=`click${en}`,Fp=`keydown${en}`,$p=`load${en}`,Bp="ArrowLeft",ca="ArrowRight",Vp="ArrowUp",ua="ArrowDown",_o="Home",da="End",Xt="active",fa="fade",wo="show",zp="dropdown",$a=".dropdown-toggle",qp=".dropdown-menu",xo=`:not(${$a})`,jp='.list-group, .nav, [role="tablist"]',Wp=".nav-item, .list-group-item",Up=`.nav-link${xo}, .list-group-item${xo}, [role="tab"]${xo}`,Ba='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',To=`${Up}, ${Ba}`,Yp=`.${Xt}[data-bs-toggle="tab"], .${Xt}[data-bs-toggle="pill"], .${Xt}[data-bs-toggle="list"]`,Jt=class n extends We{constructor(e){super(e),this._parent=this._element.closest(jp),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),T.on(this._element,Fp,t=>this._keydown(t)))}static get NAME(){return Mp}show(){let e=this._element;if(this._elemIsActive(e))return;let t=this._getActiveElem(),i=t?T.trigger(t,kp,{relatedTarget:e}):null;T.trigger(e,Ip,{relatedTarget:t}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(t,e),this._activate(e,t))}_activate(e,t){if(!e)return;e.classList.add(Xt),this._activate(z.getElementFromSelector(e));let i=()=>{if(e.getAttribute("role")!=="tab"){e.classList.add(wo);return}e.removeAttribute("tabindex"),e.setAttribute("aria-selected",!0),this._toggleDropDown(e,!0),T.trigger(e,Rp,{relatedTarget:t})};this._queueCallback(i,e,e.classList.contains(fa))}_deactivate(e,t){if(!e)return;e.classList.remove(Xt),e.blur(),this._deactivate(z.getElementFromSelector(e));let i=()=>{if(e.getAttribute("role")!=="tab"){e.classList.remove(wo);return}e.setAttribute("aria-selected",!1),e.setAttribute("tabindex","-1"),this._toggleDropDown(e,!1),T.trigger(e,Hp,{relatedTarget:t})};this._queueCallback(i,e,e.classList.contains(fa))}_keydown(e){if(![Bp,ca,Vp,ua,_o,da].includes(e.key))return;e.stopPropagation(),e.preventDefault();let t=this._getChildren().filter(r=>!Ct(r)),i;if([_o,da].includes(e.key))i=t[e.key===_o?0:t.length-1];else{let r=[ca,ua].includes(e.key);i=Oo(t,e.target,r,!0)}i&&(i.focus({preventScroll:!0}),n.getOrCreateInstance(i).show())}_getChildren(){return z.find(To,this._parent)}_getActiveElem(){return this._getChildren().find(e=>this._elemIsActive(e))||null}_setInitialAttributes(e,t){this._setAttributeIfNotExists(e,"role","tablist");for(let i of t)this._setInitialAttributesOnChild(i)}_setInitialAttributesOnChild(e){e=this._getInnerElement(e);let t=this._elemIsActive(e),i=this._getOuterElement(e);e.setAttribute("aria-selected",t),i!==e&&this._setAttributeIfNotExists(i,"role","presentation"),t||e.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(e,"role","tab"),this._setInitialAttributesOnTargetPanel(e)}_setInitialAttributesOnTargetPanel(e){let t=z.getElementFromSelector(e);t&&(this._setAttributeIfNotExists(t,"role","tabpanel"),e.id&&this._setAttributeIfNotExists(t,"aria-labelledby",`${e.id}`))}_toggleDropDown(e,t){let i=this._getOuterElement(e);if(!i.classList.contains(zp))return;let r=(o,s)=>{let a=z.findOne(o,i);a&&a.classList.toggle(s,t)};r($a,Xt),r(qp,wo),i.setAttribute("aria-expanded",t)}_setAttributeIfNotExists(e,t,i){e.hasAttribute(t)||e.setAttribute(t,i)}_elemIsActive(e){return e.classList.contains(Xt)}_getInnerElement(e){return e.matches(To)?e:z.findOne(To,e)}_getOuterElement(e){return e.closest(Wp)||e}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}};T.on(document,Pp,Ba,function(n){["A","AREA"].includes(this.tagName)&&n.preventDefault(),!Ct(this)&&Jt.getOrCreateInstance(this).show()});T.on(window,$p,()=>{for(let n of z.find(Yp))Jt.getOrCreateInstance(n)});Je(Jt);var Gp="toast",Kp="bs.toast",Lt=`.${Kp}`,Xp=`mouseover${Lt}`,Qp=`mouseout${Lt}`,Jp=`focusin${Lt}`,Zp=`focusout${Lt}`,em=`hide${Lt}`,tm=`hidden${Lt}`,nm=`show${Lt}`,im=`shown${Lt}`,rm="fade",ha="hide",Wi="show",Ui="showing",om={animation:"boolean",autohide:"boolean",delay:"number"},sm={animation:!0,autohide:!0,delay:5e3},pt=class n extends We{constructor(e,t){super(e,t),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return sm}static get DefaultType(){return om}static get NAME(){return Gp}show(){if(T.trigger(this._element,nm).defaultPrevented)return;this._clearTimeout(),this._config.animation&&this._element.classList.add(rm);let t=()=>{this._element.classList.remove(Ui),T.trigger(this._element,im),this._maybeScheduleHide()};this._element.classList.remove(ha),ni(this._element),this._element.classList.add(Wi,Ui),this._queueCallback(t,this._element,this._config.animation)}hide(){if(!this.isShown()||T.trigger(this._element,em).defaultPrevented)return;let t=()=>{this._element.classList.add(ha),this._element.classList.remove(Ui,Wi),T.trigger(this._element,tm)};this._element.classList.add(Ui),this._queueCallback(t,this._element,this._config.animation)}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(Wi),super.dispose()}isShown(){return this._element.classList.contains(Wi)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout(()=>{this.hide()},this._config.delay)))}_onInteraction(e,t){switch(e.type){case"mouseover":case"mouseout":{this._hasMouseInteraction=t;break}case"focusin":case"focusout":{this._hasKeyboardInteraction=t;break}}if(t){this._clearTimeout();return}let i=e.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){T.on(this._element,Xp,e=>this._onInteraction(e,!0)),T.on(this._element,Qp,e=>this._onInteraction(e,!1)),T.on(this._element,Jp,e=>this._onInteraction(e,!0)),T.on(this._element,Zp,e=>this._onInteraction(e,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e](this)}})}};ir(pt);Je(pt);var am=(function(){"use strict";let htmx={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(n,e){return getInputValues(n,e||"post").values},remove:null,addClass:null,removeClass:null,toggleClass:null,takeClass:null,swap:null,defineExtension:null,removeExtension:null,logAll:null,logNone:null,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,allowScriptTags:!0,inlineScriptNonce:"",inlineStyleNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",scrollBehavior:"instant",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get","delete"],selfRequestsOnly:!0,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null,disableInheritance:!1,responseHandling:[{code:"204",swap:!1},{code:"[23]..",swap:!0},{code:"[45]..",swap:!1,error:!0}],allowNestedOobSwaps:!0,historyRestoreAsHxRequest:!0,reportValidityOfForms:!1},parseInterval:null,location,_:null,version:"2.0.10"};htmx.onLoad=onLoadHelper,htmx.process=processNode,htmx.on=addEventListenerImpl,htmx.off=removeEventListenerImpl,htmx.trigger=triggerEvent,htmx.ajax=ajaxHelper,htmx.find=find,htmx.findAll=findAll,htmx.closest=closest,htmx.remove=removeElement,htmx.addClass=addClassToElement,htmx.removeClass=removeClassFromElement,htmx.toggleClass=toggleClassOnElement,htmx.takeClass=takeClassForElement,htmx.swap=swap,htmx.defineExtension=defineExtension,htmx.removeExtension=removeExtension,htmx.logAll=logAll,htmx.logNone=logNone,htmx.parseInterval=parseInterval,htmx._=internalEval;let internalAPI={addTriggerHandler,bodyContains,canAccessLocalStorage,findThisElement,filterValues,swap,hasAttribute,getAttributeValue,getClosestAttributeValue,getClosestMatch,getExpressionVars,getHeaders,getInputValues,getInternalData,getSwapSpecification,getTriggerSpecs,getTarget,makeFragment,mergeObjects,makeSettleInfo,oobSwap,querySelectorExt,settleImmediately,shouldCancel,triggerEvent,triggerErrorEvent,withExtensions},VERBS=["get","post","put","delete","patch"],VERB_SELECTOR=VERBS.map(function(n){return"[hx-"+n+"], [data-hx-"+n+"]"}).join(", ");function parseInterval(n){if(n==null)return;let e=NaN;return n.slice(-2)=="ms"?e=parseFloat(n.slice(0,-2)):n.slice(-1)=="s"?e=parseFloat(n.slice(0,-1))*1e3:n.slice(-1)=="m"?e=parseFloat(n.slice(0,-1))*1e3*60:e=parseFloat(n),isNaN(e)?void 0:e}function getRawAttribute(n,e){return n instanceof Element&&n.getAttribute(e)}function hasAttribute(n,e){return!!n.hasAttribute&&(n.hasAttribute(e)||n.hasAttribute("data-"+e))}function getAttributeValue(n,e){return getRawAttribute(n,e)||getRawAttribute(n,"data-"+e)}function parentElt(n){let e=n.parentElement;return!e&&n.parentNode instanceof ShadowRoot?n.parentNode:e}function getDocument(){return document}function getRootNode(n,e){return n.getRootNode?n.getRootNode({composed:e}):getDocument()}function getClosestMatch(n,e){for(;n&&!e(n);)n=parentElt(n);return n||null}function getAttributeValueWithDisinheritance(n,e,t){let i=getAttributeValue(e,t),r=getAttributeValue(e,"hx-disinherit");var o=getAttributeValue(e,"hx-inherit");if(n!==e){if(htmx.config.disableInheritance)return o&&(o==="*"||o.split(" ").indexOf(t)>=0)?i:null;if(r&&(r==="*"||r.split(" ").indexOf(t)>=0))return"unset"}return i}function getClosestAttributeValue(n,e){let t=null;if(getClosestMatch(n,function(i){return!!(t=getAttributeValueWithDisinheritance(n,asElement(i),e))}),t!=="unset")return t}function matches(n,e){return n instanceof Element&&n.matches(e)}function getStartTag(n){let t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i.exec(n);return t?t[1].toLowerCase():""}function parseHTML(n){return"parseHTMLUnsafe"in Document?Document.parseHTMLUnsafe(n):new DOMParser().parseFromString(n,"text/html")}function takeChildrenFor(n,e){for(;e.childNodes.length>0;)n.append(e.childNodes[0])}function duplicateScript(n){let e=getDocument().createElement("script");return forEach(n.attributes,function(t){e.setAttribute(t.name,t.value)}),e.textContent=n.textContent,e.async=!1,htmx.config.inlineScriptNonce&&(e.nonce=htmx.config.inlineScriptNonce),e}function isJavaScriptScriptNode(n){return n.matches("script")&&(n.type==="text/javascript"||n.type==="module"||n.type==="")}function normalizeScriptTags(n){Array.from(n.querySelectorAll("script")).forEach(e=>{if(isJavaScriptScriptNode(e)){let t=duplicateScript(e),i=e.parentNode;try{i.insertBefore(t,e)}catch(r){logError(r)}finally{e.remove()}}})}function makeFragment(n){let e=n.replace(/]*)?>[\s\S]*?<\/head>/i,""),t=getStartTag(e),i;if(t==="html"){i=new DocumentFragment;let o=parseHTML(n);takeChildrenFor(i,o.body),i.title=o.title}else if(t==="body"){i=new DocumentFragment;let o=parseHTML(e);takeChildrenFor(i,o.body),i.title=o.title}else{let o=parseHTML('");i=o.querySelector("template").content,i.title=o.title;var r=i.querySelector("title");r&&r.parentNode===i&&(r.remove(),i.title=r.innerText)}return i&&(htmx.config.allowScriptTags?normalizeScriptTags(i):i.querySelectorAll("script").forEach(o=>o.remove())),i}function maybeCall(n){n&&n()}function isType(n,e){return Object.prototype.toString.call(n)==="[object "+e+"]"}function isFunction(n){return typeof n=="function"}function isRawObject(n){return isType(n,"Object")}function getInternalData(n){let e="htmx-internal-data",t=n[e];return t||(t=n[e]={}),t}function toArray(n){let e=[];if(n)for(let t=0;t=0}function bodyContains(n){return n.getRootNode({composed:!0})===document}function splitOnWhitespace(n){return n.trim().split(/\s+/)}function mergeObjects(n,e){for(let t in e)e.hasOwnProperty(t)&&(n[t]=e[t]);return n}function parseJSON(n){try{return JSON.parse(n)}catch(e){return logError(e),null}}function canAccessLocalStorage(){let n="htmx:sessionStorageTest";try{return sessionStorage.setItem(n,n),sessionStorage.removeItem(n),!0}catch(e){return!1}}function normalizePath(n){try{let e=new URL(n,window.location.href);n=e.pathname+e.search}catch(e){}return n!="/"&&(n=n.replace(/\/+$/,"")),n}function internalEval(str){return maybeEval(getDocument().body,function(){return eval(str)})}function onLoadHelper(n){return htmx.on("htmx:load",function(t){n(t.detail.elt)})}function logAll(){htmx.logger=function(n,e,t){console&&console.log(e,n,t)}}function logNone(){htmx.logger=null}function find(n,e){return typeof n!="string"?n.querySelector(e):find(getDocument(),n)}function findAll(n,e){return typeof n!="string"?n.querySelectorAll(e):findAll(getDocument(),n)}function getWindow(){return window}function removeElement(n,e){n=resolveTarget(n),e?getWindow().setTimeout(function(){removeElement(n),n=null},e):parentElt(n).removeChild(n)}function asElement(n){return n instanceof Element?n:null}function asHtmlElement(n){return n instanceof HTMLElement?n:null}function asString(n){return typeof n=="string"?n:null}function asParentNode(n){return n instanceof Element||n instanceof Document||n instanceof DocumentFragment?n:null}function addClassToElement(n,e,t){n=asElement(resolveTarget(n)),n&&(t?getWindow().setTimeout(function(){addClassToElement(n,e),n=null},t):n.classList&&n.classList.add(e))}function removeClassFromElement(n,e,t){let i=asElement(resolveTarget(n));i&&(t?getWindow().setTimeout(function(){removeClassFromElement(i,e),i=null},t):i.classList&&(i.classList.remove(e),i.classList.length===0&&i.removeAttribute("class")))}function toggleClassOnElement(n,e){n=resolveTarget(n),n.classList.toggle(e)}function takeClassForElement(n,e){n=resolveTarget(n),forEach(n.parentElement.children,function(t){removeClassFromElement(t,e)}),addClassToElement(asElement(n),e)}function closest(n,e){return n=asElement(resolveTarget(n)),n?n.closest(e):null}function startsWith(n,e){return n.substring(0,e.length)===e}function endsWith(n,e){return n.substring(n.length-e.length)===e}function normalizeSelector(n){let e=n.trim();return startsWith(e,"<")&&endsWith(e,"/>")?e.substring(1,e.length-2):e}function querySelectorAllExt(n,e,t){if(e.indexOf("global ")===0)return querySelectorAllExt(n,e.slice(7),!0);n=resolveTarget(n);let i=[];{let s=0,a=0;for(let l=0;l"&&s--}a0;){let s=normalizeSelector(i.shift()),a;s.indexOf("closest ")===0?a=closest(asElement(n),normalizeSelector(s.slice(8))):s.indexOf("find ")===0?a=find(asParentNode(n),normalizeSelector(s.slice(5))):s==="next"||s==="nextElementSibling"?a=asElement(n).nextElementSibling:s.indexOf("next ")===0?a=scanForwardQuery(n,normalizeSelector(s.slice(5)),!!t):s==="previous"||s==="previousElementSibling"?a=asElement(n).previousElementSibling:s.indexOf("previous ")===0?a=scanBackwardsQuery(n,normalizeSelector(s.slice(9)),!!t):s==="document"?a=document:s==="window"?a=window:s==="body"?a=document.body:s==="root"?a=getRootNode(n,!!t):s==="host"?a=n.getRootNode().host:o.push(s),a&&r.push(a)}if(o.length>0){let s=o.join(","),a=asParentNode(getRootNode(n,!!t));r.push(...toArray(a.querySelectorAll(s)))}return r}var scanForwardQuery=function(n,e,t){let i=asParentNode(getRootNode(n,t)).querySelectorAll(e);for(let r=0;r=0;r--){let o=i[r];if(o.compareDocumentPosition(n)===Node.DOCUMENT_POSITION_FOLLOWING)return o}};function querySelectorExt(n,e){return typeof n!="string"?querySelectorAllExt(n,e)[0]:querySelectorAllExt(getDocument().body,n)[0]}function resolveTarget(n,e){return typeof n=="string"?find(asParentNode(e)||document,n):n}function processEventArgs(n,e,t,i){return isFunction(e)?{target:getDocument().body,event:asString(n),listener:e,options:t}:{target:resolveTarget(n),event:asString(e),listener:t,options:i}}function addEventListenerImpl(n,e,t,i){return ready(function(){let o=processEventArgs(n,e,t,i);o.target.addEventListener(o.event,o.listener,o.options)}),isFunction(e)?e:t}function removeEventListenerImpl(n,e,t){return ready(function(){let i=processEventArgs(n,e,t);i.target.removeEventListener(i.event,i.listener)}),isFunction(e)?e:t}let DUMMY_ELT=getDocument().createElement("output");function findAttributeTargets(n,e){let t=getClosestAttributeValue(n,e);if(t){if(t==="this")return[findThisElement(n,e)];{let i=querySelectorAllExt(n,t);if(/(^|,)(\s*)inherit(\s*)($|,)/.test(t)){let o=asElement(getClosestMatch(n,function(s){return s!==n&&hasAttribute(asElement(s),e)}));o&&i.push(...findAttributeTargets(o,e))}return i.length===0?(logError('The selector "'+t+'" on '+e+" returned no matches!"),[DUMMY_ELT]):i}}}function findThisElement(n,e){return asElement(getClosestMatch(n,function(t){return getAttributeValue(asElement(t),e)!=null}))}function getTarget(n){let e=getClosestAttributeValue(n,"hx-target");return e?e==="this"?findThisElement(n,"hx-target"):querySelectorExt(n,e):getInternalData(n).boosted?getDocument().body:n}function shouldSettleAttribute(n){return htmx.config.attributesToSettle.includes(n)}function cloneAttributes(n,e){forEach(Array.from(n.attributes),function(t){!e.hasAttribute(t.name)&&shouldSettleAttribute(t.name)&&n.removeAttribute(t.name)}),forEach(e.attributes,function(t){shouldSettleAttribute(t.name)&&n.setAttribute(t.name,t.value)})}function isInlineSwap(n,e){let t=getExtensions(e);for(let i=0;i0?(o=n.substring(0,n.indexOf(":")),r=n.substring(n.indexOf(":")+1)):o=n),e.removeAttribute("hx-swap-oob"),e.removeAttribute("data-hx-swap-oob");let s=querySelectorAllExt(i,r,!1);return s.length?(forEach(s,function(a){let l,c=e.cloneNode(!0);l=getDocument().createDocumentFragment(),l.appendChild(c),isInlineSwap(o,a)||(l=asParentNode(c));let u={shouldSwap:!0,target:a,fragment:l};triggerEvent(a,"htmx:oobBeforeSwap",u)&&(a=u.target,u.shouldSwap&&(handlePreservedElements(l),swapWithStyle(o,a,a,l,t),restorePreservedElements()),forEach(t.elts,function(d){triggerEvent(d,"htmx:oobAfterSwap",u)}))}),e.parentNode.removeChild(e)):(e.parentNode.removeChild(e),triggerErrorEvent(getDocument().body,"htmx:oobErrorNoTarget",{content:e,target:r})),n}function restorePreservedElements(){let n=find("#--htmx-preserve-pantry--");if(n){for(let e of[...n.children]){let t=find("#"+e.id);t.parentNode.moveBefore(e,t),t.remove()}n.remove()}}function handlePreservedElements(n){forEach(findAll(n,"[hx-preserve], [data-hx-preserve]"),function(e){let t=getAttributeValue(e,"id"),i=getDocument().getElementById(t);if(i!=null)if(e.moveBefore){let r=find("#--htmx-preserve-pantry--");r==null&&(getDocument().body.insertAdjacentHTML("afterend","
"),r=find("#--htmx-preserve-pantry--")),r.moveBefore(i,null)}else e.parentNode.replaceChild(i,e)})}function handleAttributes(n,e,t){forEach(e.querySelectorAll("[id]"),function(i){let r=getRawAttribute(i,"id");if(r&&r.length>0){let o=asParentNode(n),s=o&&o.querySelector(CSS.escape(i.tagName)+"#"+CSS.escape(r));if(s&&s!==o){let a=i.cloneNode();cloneAttributes(i,s),t.tasks.push(function(){cloneAttributes(i,a)})}}})}function makeAjaxLoadTask(n){return function(){removeClassFromElement(n,htmx.config.addedClass),processNode(asElement(n)),processFocus(asParentNode(n)),triggerEvent(n,"htmx:load")}}function processFocus(n){let e="[autofocus]",t=asHtmlElement(matches(n,e)?n:n.querySelector(e));t!=null&&t.focus()}function insertNodesBefore(n,e,t,i){for(handleAttributes(n,t,i);t.childNodes.length>0;){let r=t.firstChild;addClassToElement(asElement(r),htmx.config.addedClass),n.insertBefore(r,e),r.nodeType!==Node.TEXT_NODE&&r.nodeType!==Node.COMMENT_NODE&&i.tasks.push(makeAjaxLoadTask(r))}}function stringHash(n,e){let t=0;for(;t0}function swap(n,e,t,i){i||(i={});let r=null,o=null,s=function(){maybeCall(i.beforeSwapCallback),n=resolveTarget(n);let c=i.contextElement?getRootNode(i.contextElement,!1):getDocument(),u=document.activeElement,d={};d={elt:u,start:u?u.selectionStart:null,end:u?u.selectionEnd:null};let p=makeSettleInfo(n);if(t.swapStyle==="textContent")n.textContent=e;else{let m=makeFragment(e);if(p.title=i.title||m.title,i.historyRequest&&(m=m.querySelector("[hx-history-elt],[data-hx-history-elt]")||m),i.selectOOB){let g=i.selectOOB.split(",");for(let _=0;_0?getWindow().setTimeout(y,t.settleDelay):y()},a=htmx.config.globalViewTransitions;t.hasOwnProperty("transition")&&(a=t.transition);let l=i.contextElement||getDocument();if(a&&triggerEvent(l,"htmx:beforeTransition",i.eventInfo)&&typeof Promise!="undefined"&&document.startViewTransition){let c=new Promise(function(d,p){r=d,o=p}),u=s;s=function(){document.startViewTransition(function(){return u(),c})}}try{t!=null&&t.swapDelay&&t.swapDelay>0?getWindow().setTimeout(s,t.swapDelay):s()}catch(c){throw triggerErrorEvent(l,"htmx:swapError",i.eventInfo),maybeCall(o),c}}function handleTriggerHeader(n,e,t){let i=n.getResponseHeader(e);if(i.indexOf("{")===0){let r=parseJSON(i);for(let o in r)if(r.hasOwnProperty(o)){let s=r[o];isRawObject(s)?t=s.target!==void 0?s.target:t:s={value:s},triggerEvent(t,o,s)}}else{let r=i.split(",");for(let o=0;o0;){let s=e[0];if(s==="]"){if(i--,i===0){o===null&&(r=r+"true"),e.shift(),r+=")})";try{let a=maybeEval(n,function(){return Function(r)()},function(){return!0});return a.source=r,a}catch(a){return triggerErrorEvent(getDocument().body,"htmx:syntax:error",{error:a,source:r}),null}}}else s==="["&&i++;isPossibleRelativeReference(s,o,t)?r+="(("+t+"."+s+") ? ("+t+"."+s+") : (window."+s+"))":r=r+s,o=e.shift()}}}function consumeUntil(n,e){let t="";for(;n.length>0&&!e.test(n[0]);)t+=n.shift();return t}function consumeCSSSelector(n){let e;return n.length>0&&COMBINED_SELECTOR_START.test(n[0])?(n.shift(),e=consumeUntil(n,COMBINED_SELECTOR_END).trim(),n.shift()):e=consumeUntil(n,WHITESPACE_OR_COMMA),e}let INPUT_SELECTOR="input, textarea, select";function parseAndCacheTrigger(n,e,t){let i=[],r=tokenizeString(e);do{consumeUntil(r,NOT_WHITESPACE);let a=r.length,l=consumeUntil(r,/[,\[\s]/);if(l!=="")if(l==="every"){let c={trigger:"every"};consumeUntil(r,NOT_WHITESPACE),c.pollInterval=parseInterval(consumeUntil(r,/[,\[\s]/)),consumeUntil(r,NOT_WHITESPACE);var o=maybeGenerateConditional(n,r,"event");o&&(c.eventFilter=o),i.push(c)}else{let c={trigger:l};var o=maybeGenerateConditional(n,r,"event");for(o&&(c.eventFilter=o),consumeUntil(r,NOT_WHITESPACE);r.length>0&&r[0]!==",";){let d=r.shift();if(d==="changed")c.changed=!0;else if(d==="once")c.once=!0;else if(d==="consume")c.consume=!0;else if(d==="delay"&&r[0]===":")r.shift(),c.delay=parseInterval(consumeUntil(r,WHITESPACE_OR_COMMA));else if(d==="from"&&r[0]===":"){if(r.shift(),COMBINED_SELECTOR_START.test(r[0]))var s=consumeCSSSelector(r);else{var s=consumeUntil(r,WHITESPACE_OR_COMMA);if(s==="closest"||s==="find"||s==="next"||s==="previous"){r.shift();let y=consumeCSSSelector(r);y.length>0&&(s+=" "+y)}}c.from=s}else d==="target"&&r[0]===":"?(r.shift(),c.target=consumeCSSSelector(r)):d==="throttle"&&r[0]===":"?(r.shift(),c.throttle=parseInterval(consumeUntil(r,WHITESPACE_OR_COMMA))):d==="queue"&&r[0]===":"?(r.shift(),c.queue=consumeUntil(r,WHITESPACE_OR_COMMA)):d==="root"&&r[0]===":"?(r.shift(),c[d]=consumeCSSSelector(r)):d==="threshold"&&r[0]===":"?(r.shift(),c[d]=consumeUntil(r,WHITESPACE_OR_COMMA)):triggerErrorEvent(n,"htmx:syntax:error",{token:r.shift()});consumeUntil(r,NOT_WHITESPACE)}i.push(c)}r.length===a&&triggerErrorEvent(n,"htmx:syntax:error",{token:r.shift()}),consumeUntil(r,NOT_WHITESPACE)}while(r[0]===","&&r.shift());return t&&(t[e]=i),i}function getTriggerSpecs(n){let e=getAttributeValue(n,"hx-trigger"),t=[];if(e){let i=htmx.config.triggerSpecsCache;t=i&&i[e]||parseAndCacheTrigger(n,e,i)}return t.length>0?t:matches(n,"form")?[{trigger:"submit"}]:matches(n,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:matches(n,INPUT_SELECTOR)?[{trigger:"change"}]:[{trigger:"click"}]}function cancelPolling(n){getInternalData(n).cancelled=!0}function processPolling(n,e,t){let i=getInternalData(n);i.timeout=getWindow().setTimeout(function(){bodyContains(n)&&i.cancelled!==!0&&(maybeFilterEvent(t,n,makeEvent("hx:poll:trigger",{triggerSpec:t,target:n}))||e(n),processPolling(n,e,t))},t.pollInterval)}function isLocalLink(n){return location.hostname===n.hostname&&getRawAttribute(n,"href")&&getRawAttribute(n,"href").indexOf("#")!==0}function eltIsDisabled(n){return closest(n,htmx.config.disableSelector)}function boostElement(n,e,t){if(n instanceof HTMLAnchorElement&&isLocalLink(n)&&(n.target===""||n.target==="_self")||n.tagName==="FORM"&&String(getRawAttribute(n,"method")).toLowerCase()!=="dialog"){e.boosted=!0;let i,r;if(n.tagName==="A")i="get",r=getRawAttribute(n,"href");else{let o=getRawAttribute(n,"method");i=o?o.toLowerCase():"get",r=getRawAttribute(n,"action"),(r==null||r==="")&&(r=location.href),i==="get"&&r.includes("?")&&(r=r.replace(/\?[^#]+/,""))}t.forEach(function(o){addEventListener(n,function(s,a){let l=asElement(s);if(eltIsDisabled(l)){cleanUpElement(l);return}issueAjaxRequest(i,r,l,a)},e,o,!0)})}}function shouldCancel(n,e){if(n.type==="submit"&&e.tagName==="FORM")return!0;if(n.type==="click"){let t=e.closest('input[type="submit"], button');if(t&&t.form&&t.type==="submit")return!0;let i=e.closest("a"),r=/^#.+/;if(i&&i.href&&!r.test(i.getAttribute("href")))return!0}return!1}function ignoreBoostedAnchorCtrlClick(n,e){return getInternalData(n).boosted&&n instanceof HTMLAnchorElement&&e.type==="click"&&(e.ctrlKey||e.metaKey)}function maybeFilterEvent(n,e,t){let i=n.eventFilter;if(i)try{return i.call(e,t)!==!0}catch(r){let o=i.source;return triggerErrorEvent(getDocument().body,"htmx:eventFilter:error",{error:r,source:o}),!0}return!1}function addEventListener(n,e,t,i,r){let o=getInternalData(n),s;i.from?s=querySelectorAllExt(n,i.from):s=[n],i.changed&&("lastValue"in o||(o.lastValue=new WeakMap),s.forEach(function(a){o.lastValue.has(i)||o.lastValue.set(i,new WeakMap),o.lastValue.get(i).set(a,a.value)})),forEach(s,function(a){let l=function(c){if(!bodyContains(n)){a.removeEventListener(i.trigger,l);return}if(ignoreBoostedAnchorCtrlClick(n,c)||((r||shouldCancel(c,a))&&c.preventDefault(),maybeFilterEvent(i,n,c)))return;let u=getInternalData(c);if(u.triggerSpec=i,u.handledFor==null&&(u.handledFor=[]),u.handledFor.indexOf(n)<0){if(u.handledFor.push(n),i.consume&&c.stopPropagation(),i.target&&c.target&&!matches(asElement(c.target),i.target))return;if(i.once){if(o.triggeredOnce)return;o.triggeredOnce=!0}if(i.changed){let d=c.target,p=d.value,y=o.lastValue.get(i);if(y.has(d)&&y.get(d)===p)return;y.set(d,p)}if(o.delayed&&clearTimeout(o.delayed),o.throttle)return;i.throttle>0?o.throttle||(triggerEvent(n,"htmx:trigger"),e(n,c),o.throttle=getWindow().setTimeout(function(){o.throttle=null},i.throttle)):i.delay>0?o.delayed=getWindow().setTimeout(function(){triggerEvent(n,"htmx:trigger"),e(n,c)},i.delay):(triggerEvent(n,"htmx:trigger"),e(n,c))}};t.listenerInfos==null&&(t.listenerInfos=[]),t.listenerInfos.push({trigger:i.trigger,listener:l,on:a}),a.addEventListener(i.trigger,l)})}let windowIsScrolling=!1,scrollHandler=null;function initScrollHandler(){scrollHandler||(scrollHandler=function(){windowIsScrolling=!0},window.addEventListener("scroll",scrollHandler),window.addEventListener("resize",scrollHandler),setInterval(function(){windowIsScrolling&&(windowIsScrolling=!1,forEach(getDocument().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),function(n){maybeReveal(n)}))},200))}function maybeReveal(n){!hasAttribute(n,"data-hx-revealed")&&isScrolledIntoView(n)&&(n.setAttribute("data-hx-revealed","true"),getInternalData(n).initHash?triggerEvent(n,"revealed"):n.addEventListener("htmx:afterProcessNode",function(){triggerEvent(n,"revealed")},{once:!0}))}function loadImmediately(n,e,t,i){let r=function(){t.loaded||(t.loaded=!0,triggerEvent(n,"htmx:trigger"),e(n))};i>0?getWindow().setTimeout(r,i):r()}function processVerbs(n,e,t){let i=!1;return forEach(VERBS,function(r){if(hasAttribute(n,"hx-"+r)){let o=getAttributeValue(n,"hx-"+r);i=!0,e.path=o,e.verb=r,t.forEach(function(s){addTriggerHandler(n,s,e,function(a,l){let c=asElement(a);if(eltIsDisabled(c)){cleanUpElement(c);return}issueAjaxRequest(r,o,c,l)})})}}),i}function addTriggerHandler(n,e,t,i){if(e.trigger==="revealed")initScrollHandler(),addEventListener(n,i,t,e),maybeReveal(asElement(n));else if(e.trigger==="intersect"){let r={};e.root&&(r.root=querySelectorExt(n,e.root)),e.threshold&&(r.threshold=parseFloat(e.threshold)),new IntersectionObserver(function(s){for(let a=0;a0?(t.polling=!0,processPolling(asElement(n),i,e)):addEventListener(n,i,t,e)}function shouldProcessHxOn(n){let e=asElement(n);if(!e)return!1;let t=e.attributes;for(let i=0;i", "+o).join(""))}else return[]}function maybeSetLastButtonClicked(n){let e=getTargetButton(n.target),t=getRelatedFormData(n);t&&(t.lastButtonClicked=e)}function maybeUnsetLastButtonClicked(n){let e=getRelatedFormData(n);e&&(e.lastButtonClicked=null)}function getTargetButton(n){return closest(asElement(n),"button, input[type='submit']")}function getRelatedForm(n){return n.form||closest(n,"form")}function getRelatedFormData(n){let e=getTargetButton(n.target);if(!e)return;let t=getRelatedForm(e);if(t)return getInternalData(t)}function initButtonTracking(n){n.addEventListener("click",maybeSetLastButtonClicked),n.addEventListener("focusin",maybeSetLastButtonClicked),n.addEventListener("focusout",maybeUnsetLastButtonClicked)}function addHxOnEventHandler(n,e,t){let i=getInternalData(n);Array.isArray(i.onHandlers)||(i.onHandlers=[]);let r,o=function(s){maybeEval(n,function(){eltIsDisabled(n)||(r||(r=new Function("event",t)),r.call(n,s))})};n.addEventListener(e,o),i.onHandlers.push({event:e,listener:o})}function processHxOnWildcard(n){deInitOnHandlers(n);for(let e=0;ehtmx.config.historyCacheSize;)o.shift();for(;o.length>0;)try{sessionStorage.setItem("htmx-history-cache",JSON.stringify(o));break}catch(a){triggerErrorEvent(getDocument().body,"htmx:historyCacheError",{cause:a,cache:o}),o.shift()}}function getCachedHistory(n){if(!canAccessLocalStorage())return null;n=normalizePath(n);let e=parseJSON(sessionStorage.getItem("htmx-history-cache"))||[];for(let t=0;t=200&&this.status<400?(i.response=this.response,triggerEvent(getDocument().body,"htmx:historyCacheMissLoad",i),swap(i.historyElt,i.response,t,{contextElement:i.historyElt,historyRequest:!0}),setCurrentPathForHistory(i.path),triggerEvent(getDocument().body,"htmx:historyRestore",{path:n,cacheMiss:!0,serverResponse:i.response})):triggerErrorEvent(getDocument().body,"htmx:historyCacheMissLoadError",i)},triggerEvent(getDocument().body,"htmx:historyCacheMiss",i)&&e.send()}function restoreHistory(n){saveCurrentPageToHistory(),n=n||location.pathname+location.search;let e=getCachedHistory(n);if(e){let t={swapStyle:"innerHTML",swapDelay:0,settleDelay:0,scroll:e.scroll},i={path:n,item:e,historyElt:getHistoryElement(),swapSpec:t};triggerEvent(getDocument().body,"htmx:historyCacheHit",i)&&(swap(i.historyElt,e.content,t,{contextElement:i.historyElt,title:e.title}),setCurrentPathForHistory(i.path),triggerEvent(getDocument().body,"htmx:historyRestore",i))}else htmx.config.refreshOnHistoryMiss?htmx.location.reload(!0):loadHistoryFromServer(n)}function addRequestIndicatorClasses(n){let e=findAttributeTargets(n,"hx-indicator");return e==null&&(e=[n]),forEach(e,function(t){let i=getInternalData(t);i.requestCount=(i.requestCount||0)+1,addClassToElement(t,htmx.config.requestClass)}),e}function disableElements(n){let e=findAttributeTargets(n,"hx-disabled-elt");return e==null&&(e=[]),forEach(e,function(t){let i=getInternalData(t);i.requestCount=(i.requestCount||0)+1,t.hasAttribute("disabled")||(t.setAttribute("disabled",""),t.setAttribute("data-disabled-by-htmx",""))}),e}function removeRequestIndicators(n,e){forEach(n.concat(e),function(t){let i=getInternalData(t);i.requestCount=(i.requestCount||1)-1}),forEach(n,function(t){getInternalData(t).requestCount===0&&removeClassFromElement(t,htmx.config.requestClass)}),forEach(e,function(t){getInternalData(t).requestCount===0&&t.hasAttribute("data-disabled-by-htmx")&&(t.removeAttribute("disabled"),t.removeAttribute("data-disabled-by-htmx"))})}function haveSeenNode(n,e){for(let t=0;te.indexOf(r)<0):i=i.filter(r=>r!==e),t.delete(n),forEach(i,r=>t.append(n,r))}}function getValueFromInput(n){return n instanceof HTMLSelectElement&&n.multiple?toArray(n.querySelectorAll("option:checked")).map(function(e){return e.value}):n instanceof HTMLInputElement&&n.files?toArray(n.files):n.value}function processInputValue(n,e,t,i,r){if(!(i==null||haveSeenNode(n,i))){if(n.push(i),shouldInclude(i)){let o=getRawAttribute(i,"name");addValueToFormData(o,getValueFromInput(i),e),r&&validateElement(i,t)}i instanceof HTMLFormElement&&(forEach(i.elements,function(o){n.indexOf(o)>=0?removeValueFromFormData(o.name,getValueFromInput(o),e):n.push(o),r&&validateElement(o,t)}),new FormData(i).forEach(function(o,s){o instanceof File&&o.name===""||addValueToFormData(s,o,e)}))}}function validateElement(n,e){let t=n;t.willValidate&&(triggerEvent(t,"htmx:validation:validate"),t.checkValidity()||(triggerEvent(t,"htmx:validation:failed",{message:t.validationMessage,validity:t.validity})&&!e.length&&htmx.config.reportValidityOfForms&&t.reportValidity(),e.push({elt:t,message:t.validationMessage,validity:t.validity})))}function overrideFormData(n,e){for(let t of e.keys())n.delete(t);return e.forEach(function(t,i){n.append(i,t)}),n}function getInputValues(n,e){let t=[],i=new FormData,r=new FormData,o=[],s=getInternalData(n);s.lastButtonClicked&&!bodyContains(s.lastButtonClicked)&&(s.lastButtonClicked=null);let a=n instanceof HTMLFormElement&&n.noValidate!==!0||getAttributeValue(n,"hx-validate")==="true";if(s.lastButtonClicked&&(a=a&&s.lastButtonClicked.formNoValidate!==!0),e!=="get"&&processInputValue(t,r,o,getRelatedForm(n),a),processInputValue(t,i,o,n,a),s.lastButtonClicked||n.tagName==="BUTTON"||n.tagName==="INPUT"&&getRawAttribute(n,"type")==="submit"){let c=s.lastButtonClicked||n,u=getRawAttribute(c,"name");addValueToFormData(u,c.value,r)}let l=findAttributeTargets(n,"hx-include");return forEach(l,function(c){processInputValue(t,i,o,asElement(c),a),matches(c,"form")||forEach(asParentNode(c).querySelectorAll(INPUT_SELECTOR),function(u){processInputValue(t,i,o,u,a)})}),overrideFormData(i,r),{errors:o,formData:i,values:formDataProxy(i)}}function appendParam(n,e,t){n!==""&&(n+="&"),String(t)==="[object Object]"&&(t=JSON.stringify(t));let i=encodeURIComponent(t);return n+=encodeURIComponent(e)+"="+i,n}function urlEncode(n){n=formDataFromObject(n);let e="";return n.forEach(function(t,i){e=appendParam(e,i,t)}),e}function getHeaders(n,e,t){let i={"HX-Request":"true","HX-Trigger":getRawAttribute(n,"id"),"HX-Trigger-Name":getRawAttribute(n,"name"),"HX-Target":getAttributeValue(e,"id"),"HX-Current-URL":location.href};return getValuesForElement(n,"hx-headers",!1,i),t!==void 0&&(i["HX-Prompt"]=t),getInternalData(n).boosted&&(i["HX-Boosted"]="true"),i}function filterValues(n,e){let t=getClosestAttributeValue(e,"hx-params");if(t){if(t==="none")return new FormData;if(t==="*")return n;if(t.indexOf("not ")===0)return forEach(t.slice(4).split(","),function(i){i=i.trim(),n.delete(i)}),n;{let i=new FormData;return forEach(t.split(","),function(r){r=r.trim(),n.has(r)&&n.getAll(r).forEach(function(o){i.append(r,o)})}),i}}else return n}function isAnchorLink(n){return!!getRawAttribute(n,"href")&&getRawAttribute(n,"href").indexOf("#")>=0}function getSwapSpecification(n,e){let t=e||getClosestAttributeValue(n,"hx-swap"),i={swapStyle:getInternalData(n).boosted?"innerHTML":htmx.config.defaultSwapStyle,swapDelay:htmx.config.defaultSwapDelay,settleDelay:htmx.config.defaultSettleDelay};if(htmx.config.scrollIntoViewOnBoost&&getInternalData(n).boosted&&!isAnchorLink(n)&&(i.show="top"),t){let s=splitOnWhitespace(t);if(s.length>0)for(let a=0;a0?r.join(":"):null;i.scroll=u,i.scrollTarget=o}else if(l.indexOf("show:")===0){var r=l.slice(5).split(":");let d=r.pop();var o=r.length>0?r.join(":"):null;i.show=d,i.showTarget=o}else if(l.indexOf("focus-scroll:")===0){let c=l.slice(13);i.focusScroll=c=="true"}else a==0?i.swapStyle=l:logError("Unknown modifier in hx-swap: "+l)}}return i}function usesFormData(n){return getClosestAttributeValue(n,"hx-encoding")==="multipart/form-data"||matches(n,"form")&&getRawAttribute(n,"enctype")==="multipart/form-data"}function encodeParamsForBody(n,e,t){let i=null;return withExtensions(e,function(r){i==null&&(i=r.encodeParameters(n,t,e))}),i!=null?i:usesFormData(e)?overrideFormData(new FormData,formDataFromObject(t)):urlEncode(t)}function makeSettleInfo(n){return{tasks:[],elts:[n]}}function updateScrollState(n,e){let t=n[0],i=n[n.length-1];if(e.scroll){var r=null;e.scrollTarget&&(r=asElement(querySelectorExt(t,e.scrollTarget))),e.scroll==="top"&&(t||r)&&(r=r||t,r.scrollTop=0),e.scroll==="bottom"&&(i||r)&&(r=r||i,r.scrollTop=r.scrollHeight),typeof e.scroll=="number"&&getWindow().setTimeout(function(){window.scrollTo(0,e.scroll)},0)}if(e.show){var r=null;if(e.showTarget){let s=e.showTarget;e.showTarget==="window"&&(s="body"),r=asElement(querySelectorExt(t,s))}e.show==="top"&&(t||r)&&(r=r||t,r.scrollIntoView({block:"start",behavior:htmx.config.scrollBehavior})),e.show==="bottom"&&(i||r)&&(r=r||i,r.scrollIntoView({block:"end",behavior:htmx.config.scrollBehavior}))}}function getValuesForElement(n,e,t,i,r){if(i==null&&(i={}),n==null)return i;let o=getAttributeValue(n,e);if(o){let s=o.trim(),a=t;if(s==="unset")return null;s.indexOf("javascript:")===0?(s=s.slice(11),a=!0):s.indexOf("js:")===0&&(s=s.slice(3),a=!0),s.indexOf("{")!==0&&(s="{"+s+"}");let l;a?l=maybeEval(n,function(){return r?Function("event","return ("+s+")").call(n,r):Function("return ("+s+")").call(n)},{}):l=parseJSON(s);for(let c in l)l.hasOwnProperty(c)&&i[c]==null&&(i[c]=l[c])}return getValuesForElement(asElement(parentElt(n)),e,t,i,r)}function maybeEval(n,e,t){return htmx.config.allowEval?e():(triggerErrorEvent(n,"htmx:evalDisallowedError"),t)}function getHXVarsForElement(n,e,t){return getValuesForElement(n,"hx-vars",!0,t,e)}function getHXValsForElement(n,e,t){return getValuesForElement(n,"hx-vals",!1,t,e)}function getExpressionVars(n,e){return mergeObjects(getHXVarsForElement(n,e),getHXValsForElement(n,e))}function safelySetHeaderValue(n,e,t){if(t!==null)try{n.setRequestHeader(e,t)}catch(i){n.setRequestHeader(e,encodeURIComponent(t)),n.setRequestHeader(e+"-URI-AutoEncoded","true")}}function getPathFromResponse(n){if(n.responseURL)try{let e=new URL(n.responseURL);return e.pathname+e.search}catch(e){triggerErrorEvent(getDocument().body,"htmx:badResponseUrl",{url:n.responseURL})}}function hasHeader(n,e){return e.test(n.getAllResponseHeaders())}function ajaxHelper(n,e,t){if(n=n.toLowerCase(),t){if(t instanceof Element||typeof t=="string")return issueAjaxRequest(n,e,null,null,{targetOverride:resolveTarget(t)||DUMMY_ELT,returnPromise:!0});{let i=resolveTarget(t.target);return(t.target&&!i||t.source&&!i&&!resolveTarget(t.source))&&(i=DUMMY_ELT),issueAjaxRequest(n,e,resolveTarget(t.source),t.event,{handler:t.handler,headers:t.headers,values:t.values,targetOverride:i,swapOverride:t.swap,select:t.select,returnPromise:!0,push:t.push,replace:t.replace,selectOOB:t.selectOOB})}}else return issueAjaxRequest(n,e,null,null,{returnPromise:!0})}function hierarchyForElt(n){let e=[];for(;n;)e.push(n),n=n.parentElement;return e}function verifyPath(n,e,t){let i=new URL(e,location.protocol!=="about:"?location.href:window.origin),o=(location.protocol!=="about:"?location.origin:window.origin)===i.origin;return htmx.config.selfRequestsOnly&&!o?!1:triggerEvent(n,"htmx:validateUrl",mergeObjects({url:i,sameHost:o},t))}function formDataFromObject(n){if(n instanceof FormData)return n;let e=new FormData;for(let t in n)n.hasOwnProperty(t)&&(n[t]&&typeof n[t].forEach=="function"?n[t].forEach(function(i){e.append(t,i)}):typeof n[t]=="object"&&!(n[t]instanceof Blob)?e.append(t,JSON.stringify(n[t])):e.append(t,n[t]));return e}function formDataArrayProxy(n,e,t){return new Proxy(t,{get:function(i,r){return typeof r=="number"?i[r]:r==="length"?i.length:r==="push"?function(o){i.push(o),n.append(e,o)}:typeof i[r]=="function"?function(){i[r].apply(i,arguments),n.delete(e),i.forEach(function(o){n.append(e,o)})}:i[r]&&i[r].length===1?i[r][0]:i[r]},set:function(i,r,o){return i[r]=o,n.delete(e),i.forEach(function(s){n.append(e,s)}),!0}})}function formDataProxy(n){return new Proxy(n,{get:function(e,t){if(typeof t=="symbol"){let r=Reflect.get(e,t);return typeof r=="function"?function(){return r.apply(n,arguments)}:r}if(t==="toJSON")return()=>Object.fromEntries(n);if(t in e&&typeof e[t]=="function")return function(){return n[t].apply(n,arguments)};let i=n.getAll(t);if(i.length!==0)return i.length===1?i[0]:formDataArrayProxy(e,t,i)},set:function(e,t,i){return typeof t!="string"?!1:(e.delete(t),i&&typeof i.forEach=="function"?i.forEach(function(r){e.append(t,r)}):typeof i=="object"&&!(i instanceof Blob)?e.append(t,JSON.stringify(i)):e.append(t,i),!0)},deleteProperty:function(e,t){return typeof t=="string"&&e.delete(t),!0},ownKeys:function(e){return Reflect.ownKeys(Object.fromEntries(e))},getOwnPropertyDescriptor:function(e,t){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(e),t)}})}function issueAjaxRequest(n,e,t,i,r,o){let s=null,a=null;if(r=r!=null?r:{},r.returnPromise&&typeof Promise!="undefined")var l=new Promise(function(Y,ee){s=Y,a=ee});t==null&&(t=getDocument().body);let c=r.handler||handleAjaxResponse,u=r.select||null;if(!bodyContains(t))return maybeCall(s),l;let d=r.targetOverride||asElement(getTarget(t));if(d==null||d==DUMMY_ELT)return triggerErrorEvent(t,"htmx:targetError",{target:getClosestAttributeValue(t,"hx-target")}),maybeCall(a),l;let p=getInternalData(t),y=p.lastButtonClicked;if(y){let Y=getRawAttribute(y,"formaction");Y!=null&&(e=Y);let ee=getRawAttribute(y,"formmethod");if(ee!=null)if(VERBS.includes(ee.toLowerCase()))n=ee;else return maybeCall(s),l}let m=getClosestAttributeValue(t,"hx-confirm");if(o===void 0&&triggerEvent(t,"htmx:confirm",{target:d,elt:t,path:e,verb:n,triggeringEvent:i,etc:r,issueRequest:function(ae){return issueAjaxRequest(n,e,t,i,r,!!ae)},question:m})===!1)return maybeCall(s),l;let g=t,_=getClosestAttributeValue(t,"hx-sync"),x=null,A=!1;if(_){let Y=_.split(":"),ee=Y[0].trim();if(ee==="this"?g=findThisElement(t,"hx-sync"):g=asElement(querySelectorExt(t,ee)),_=(Y[1]||"drop").trim(),p=getInternalData(g),_==="drop"&&p.xhr&&p.abortable!==!0)return maybeCall(s),l;if(_==="abort"){if(p.xhr)return maybeCall(s),l;A=!0}else _==="replace"?triggerEvent(g,"htmx:abort"):_.indexOf("queue")===0&&(x=(_.split(" ")[1]||"last").trim())}if(p.xhr)if(p.abortable)triggerEvent(g,"htmx:abort");else{if(x==null){if(i){let Y=getInternalData(i);Y&&Y.triggerSpec&&Y.triggerSpec.queue&&(x=Y.triggerSpec.queue)}x==null&&(x="last")}return p.queuedRequests==null&&(p.queuedRequests=[]),x==="first"&&p.queuedRequests.length===0?p.queuedRequests.push(function(){issueAjaxRequest(n,e,t,i,r)}):x==="all"?p.queuedRequests.push(function(){issueAjaxRequest(n,e,t,i,r)}):x==="last"&&(p.queuedRequests=[],p.queuedRequests.push(function(){issueAjaxRequest(n,e,t,i,r)})),maybeCall(s),l}let w=new XMLHttpRequest;p.xhr=w,p.abortable=A;let C=function(){p.xhr=null,p.abortable=!1,p.queuedRequests!=null&&p.queuedRequests.length>0&&p.queuedRequests.shift()()},$=getClosestAttributeValue(t,"hx-prompt");if($){var q=prompt($);if(q===null||!triggerEvent(t,"htmx:prompt",{prompt:q,target:d}))return maybeCall(s),C(),l}if(m&&!o&&!confirm(m))return maybeCall(s),C(),l;let H=getHeaders(t,d,q);n!=="get"&&!usesFormData(t)&&(H["Content-Type"]="application/x-www-form-urlencoded"),r.headers&&(H=mergeObjects(H,r.headers));let I=getInputValues(t,n),L=I.errors,j=I.formData;r.values&&overrideFormData(j,formDataFromObject(r.values));let G=formDataFromObject(getExpressionVars(t,i)),Q=overrideFormData(j,G),Z=filterValues(Q,t);htmx.config.getCacheBusterParam&&n==="get"&&Z.set("org.htmx.cache-buster",getRawAttribute(d,"id")||"true"),(e==null||e==="")&&(e=location.href);let he=getValuesForElement(t,"hx-request"),Ce=getInternalData(t).boosted,ie=htmx.config.methodsThatUseUrlParams.indexOf(n)>=0,ne={boosted:Ce,useUrlParams:ie,formData:Z,parameters:formDataProxy(Z),unfilteredFormData:Q,unfilteredParameters:formDataProxy(Q),headers:H,elt:t,target:d,verb:n,errors:L,withCredentials:r.credentials||he.credentials||htmx.config.withCredentials,timeout:r.timeout||he.timeout||htmx.config.timeout,path:e,triggeringEvent:i};if(!triggerEvent(t,"htmx:configRequest",ne))return maybeCall(s),C(),l;if(e=ne.path,n=ne.verb,H=ne.headers,Z=formDataFromObject(ne.parameters),L=ne.errors,ie=ne.useUrlParams,L&&L.length>0)return triggerEvent(t,"htmx:validation:halted",ne),maybeCall(s),C(),l;let Ue=e.split("#"),Re=Ue[0],W=Ue[1],M=e;if(ie&&(M=Re,!Z.keys().next().done&&(M.indexOf("?")<0?M+="?":M+="&",M+=urlEncode(Z),W&&(M+="#"+W))),!verifyPath(t,M,ne))return triggerErrorEvent(t,"htmx:invalidPath",ne),maybeCall(a),C(),l;if(w.open(n.toUpperCase(),M,!0),w.overrideMimeType("text/html"),w.withCredentials=ne.withCredentials,w.timeout=ne.timeout,!he.noHeaders){for(let Y in H)if(H.hasOwnProperty(Y)){let ee=H[Y];safelySetHeaderValue(w,Y,ee)}}let D={xhr:w,target:d,requestConfig:ne,etc:r,boosted:Ce,select:u,pathInfo:{requestPath:e,finalRequestPath:M,responsePath:null,anchor:W}};if(w.onload=function(){try{let Y=hierarchyForElt(t);if(D.pathInfo.responsePath=getPathFromResponse(w),c(t,D),D.keepIndicators!==!0&&removeRequestIndicators(B,V),triggerEvent(t,"htmx:afterRequest",D),triggerEvent(t,"htmx:afterOnLoad",D),!bodyContains(t)){let ee=null;for(;Y.length>0&&ee==null;){let ae=Y.shift();bodyContains(ae)&&(ee=ae)}ee&&(triggerEvent(ee,"htmx:afterRequest",D),triggerEvent(ee,"htmx:afterOnLoad",D))}maybeCall(s)}catch(Y){throw triggerErrorEvent(t,"htmx:onLoadError",mergeObjects({error:Y},D)),Y}finally{C()}},w.onerror=function(){removeRequestIndicators(B,V),triggerErrorEvent(t,"htmx:afterRequest",D),triggerErrorEvent(t,"htmx:sendError",D),maybeCall(a),C()},w.onabort=function(){removeRequestIndicators(B,V),triggerErrorEvent(t,"htmx:afterRequest",D),triggerErrorEvent(t,"htmx:sendAbort",D),maybeCall(a),C()},w.ontimeout=function(){removeRequestIndicators(B,V),triggerErrorEvent(t,"htmx:afterRequest",D),triggerErrorEvent(t,"htmx:timeout",D),maybeCall(a),C()},!triggerEvent(t,"htmx:beforeRequest",D))return maybeCall(s),C(),l;var B=addRequestIndicatorClasses(t),V=disableElements(t);forEach(["loadstart","loadend","progress","abort"],function(Y){forEach([w,w.upload],function(ee){ee.addEventListener(Y,function(ae){triggerEvent(t,"htmx:xhr:"+Y,{lengthComputable:ae.lengthComputable,loaded:ae.loaded,total:ae.total})})})}),triggerEvent(t,"htmx:beforeSend",D);let U=ie?null:encodeParamsForBody(w,t,Z);return w.send(U),l}function determineHistoryUpdates(n,e){let t=e.xhr,i=null,r=null;if(hasHeader(t,/HX-Push:/i)?(i=t.getResponseHeader("HX-Push"),r="push"):hasHeader(t,/HX-Push-Url:/i)?(i=t.getResponseHeader("HX-Push-Url"),r="push"):hasHeader(t,/HX-Replace-Url:/i)&&(i=t.getResponseHeader("HX-Replace-Url"),r="replace"),i)return i==="false"?{}:{type:r,path:i};let o=e.pathInfo.finalRequestPath,s=e.pathInfo.responsePath,a=e.etc.push||getClosestAttributeValue(n,"hx-push-url"),l=e.etc.replace||getClosestAttributeValue(n,"hx-replace-url");a==="false"&&(a=null),l==="false"&&(l=null);let c=getInternalData(n).boosted,u=null,d=null;return a?(u="push",d=a):l?(u="replace",d=l):c&&(u="push",d=s||o),d?(d==="true"&&(d=s||o),e.pathInfo.anchor&&d.indexOf("#")===-1&&(d=d+"#"+e.pathInfo.anchor),{type:u,path:d}):{}}function codeMatches(n,e){var t=new RegExp(n.code);return t.test(e.toString(10))}function resolveResponseHandling(n){for(var e=0;e.${e}{opacity:0;visibility: hidden} .${t} .${e}, .${t}.${e}{opacity:1;visibility: visible;transition: opacity 200ms ease-in}`)}}function getMetaConfig(){let n=getDocument().querySelector('meta[name="htmx-config"]');return n?parseJSON(n.content):null}function mergeMetaConfig(){let n=getMetaConfig();n&&(htmx.config=mergeObjects(htmx.config,n))}return ready(function(){mergeMetaConfig(),insertIndicatorStyles();let n=getDocument().body;processNode(n);let e=getDocument().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");n.addEventListener("htmx:abort",function(i){let r=i.detail.elt||i.target,o=getInternalData(r);o&&o.xhr&&o.xhr.abort()});let t=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(i){i.state&&i.state.htmx?(restoreHistory(),forEach(e,function(r){triggerEvent(r,"htmx:restored",{document:getDocument(),triggerEvent})})):t&&t(i)},getWindow().setTimeout(function(){triggerEvent(n,"htmx:load",{}),n=null},0)}),htmx})(),U_=am;function ko(n,e){n.split(/\s+/).forEach(t=>{e(t)})}var ri=class{constructor(){this._events={}}on(e,t){ko(e,i=>{let r=this._events[i]||[];r.push(t),this._events[i]=r})}off(e,t){var i=arguments.length;if(i===0){this._events={};return}ko(e,r=>{if(i===1){delete this._events[r];return}let o=this._events[r];o!==void 0&&(o.splice(o.indexOf(t),1),this._events[r]=o)})}trigger(e,...t){var i=this;ko(e,r=>{let o=i._events[r];o!==void 0&&o.forEach(s=>{s.apply(i,t)})})}};function Ho(n){return n.plugins={},class extends n{constructor(){super(...arguments),this.plugins={names:[],settings:{},requested:{},loaded:{}}}static define(e,t){n.plugins[e]={name:e,fn:t}}initializePlugins(e){var t,i;let r=this,o=[];if(Array.isArray(e))e.forEach(s=>{typeof s=="string"?o.push(s):(r.plugins.settings[s.name]=s.options,o.push(s.name))});else if(e)for(t in e)e.hasOwnProperty(t)&&(r.plugins.settings[t]=e[t],o.push(t));for(;i=o.shift();)r.require(i)}loadPlugin(e){var t=this,i=t.plugins,r=n.plugins[e];if(!n.plugins.hasOwnProperty(e))throw new Error('Unable to find "'+e+'" plugin');i.requested[e]=!0,i.loaded[e]=r.fn.apply(t,[t.plugins.settings[e]||{}]),i.names.push(e)}require(e){var t=this,i=t.plugins;if(!t.plugins.loaded.hasOwnProperty(e)){if(i.requested[e])throw new Error('Plugin has circular dependency ("'+e+'")');t.loadPlugin(e)}return i.loaded[e]}}}var oi=n=>(n=n.filter(Boolean),n.length<2?n[0]||"":cm(n)==1?"["+n.join("")+"]":"(?:"+n.join("|")+")"),Io=n=>{if(!lm(n))return n.join("");let e="",t=0,i=()=>{t>1&&(e+="{"+t+"}")};return n.forEach((r,o)=>{if(r===n[o-1]){t++;return}i(),e+=r,t=1}),i(),e},Ro=n=>{let e=Array.from(n);return oi(e)},lm=n=>new Set(n).size!==n.length,Mt=n=>(n+"").replace(/([\$\(\)\*\+\.\?\[\]\^\{\|\}\\])/gu,"\\$1"),cm=n=>n.reduce((e,t)=>Math.max(e,um(t)),0),um=n=>Array.from(n).length;var Po=n=>{if(n.length===1)return[[n]];let e=[],t=n.substring(1);return Po(t).forEach(function(r){let o=r.slice(0);o[0]=n.charAt(0)+o[0],e.push(o),o=r.slice(0),o.unshift(n.charAt(0)),e.push(o)}),e};var dm=[[0,65535]],fm="[\u0300-\u036F\xB7\u02BE\u02BC]",rr,ja,hm=3,Fo={},Va={"/":"\u2044\u2215",0:"\u07C0",a:"\u2C65\u0250\u0251",aa:"\uA733",ae:"\xE6\u01FD\u01E3",ao:"\uA735",au:"\uA737",av:"\uA739\uA73B",ay:"\uA73D",b:"\u0180\u0253\u0183",c:"\uA73F\u0188\u023C\u2184",d:"\u0111\u0257\u0256\u1D05\u018C\uABB7\u0501\u0266",e:"\u025B\u01DD\u1D07\u0247",f:"\uA77C\u0192",g:"\u01E5\u0260\uA7A1\u1D79\uA77F\u0262",h:"\u0127\u2C68\u2C76\u0265",i:"\u0268\u0131",j:"\u0249\u0237",k:"\u0199\u2C6A\uA741\uA743\uA745\uA7A3",l:"\u0142\u019A\u026B\u2C61\uA749\uA747\uA781\u026D",m:"\u0271\u026F\u03FB",n:"\uA7A5\u019E\u0272\uA791\u1D0E\u043B\u0509",o:"\xF8\u01FF\u0254\u0275\uA74B\uA74D\u1D11",oe:"\u0153",oi:"\u01A3",oo:"\uA74F",ou:"\u0223",p:"\u01A5\u1D7D\uA751\uA753\uA755\u03C1",q:"\uA757\uA759\u024B",r:"\u024D\u027D\uA75B\uA7A7\uA783",s:"\xDF\u023F\uA7A9\uA785\u0282",t:"\u0167\u01AD\u0288\u2C66\uA787",th:"\xFE",tz:"\uA729",u:"\u0289",v:"\u028B\uA75F\u028C",vy:"\uA761",w:"\u2C73",y:"\u01B4\u024F\u1EFF",z:"\u01B6\u0225\u0240\u2C6C\uA763",hv:"\u0195"};for(let n in Va){let e=Va[n]||"";for(let t=0;t{rr===void 0&&(rr=Em(n||dm))},za=(n,e="NFKD")=>n.normalize(e),si=n=>Array.from(n).reduce((e,t)=>e+gm(t),""),gm=n=>(n=za(n).toLowerCase().replace(pm,e=>Fo[e]||""),za(n,"NFC"));function*vm(n){for(let[e,t]of n)for(let i=e;i<=t;i++){let r=String.fromCharCode(i),o=si(r);o!=r.toLowerCase()&&(o.length>hm||o.length!=0&&(yield{folded:o,composed:r,code_point:i}))}}var ym=n=>{let e={},t=(i,r)=>{let o=e[i]||new Set,s=new RegExp("^"+Ro(o)+"$","iu");r.match(s)||(o.add(Mt(r)),e[i]=o)};for(let i of vm(n))t(i.folded,i.folded),t(i.folded,i.composed);return e},Em=n=>{let e=ym(n),t={},i=[];for(let o in e){let s=e[o];s&&(t[o]=Ro(s)),o.length>1&&i.push(Mt(o))}i.sort((o,s)=>s.length-o.length);let r=oi(i);return ja=new RegExp("^"+r,"u"),t},bm=(n,e=1)=>{let t=0;return n=n.map(i=>(rr[i]&&(t+=i.length),rr[i]||i)),t>=e?Io(n):""},_m=(n,e=1)=>(e=Math.max(e,n.length-1),oi(Po(n).map(t=>bm(t,e)))),qa=(n,e=!0)=>{let t=n.length>1?1:0;return oi(n.map(i=>{let r=[],o=e?i.length():i.length()-1;for(let s=0;s{for(let t of e){if(t.start!=n.start||t.end!=n.end||t.substrs.join("")!==n.substrs.join(""))continue;let i=n.parts,r=s=>{for(let a of i){if(a.start===s.start&&a.substr===s.substr)return!1;if(!(s.length==1||a.length==1)&&(s.starta.start||a.starts.start))return!0}return!1};if(!(t.parts.filter(r).length>0))return!0}return!1},or=class n{constructor(){se(this,"parts");se(this,"substrs");se(this,"start");se(this,"end");this.parts=[],this.substrs=[],this.start=0,this.end=0}add(e){e&&(this.parts.push(e),this.substrs.push(e.substr),this.start=Math.min(e.start,this.start),this.end=Math.max(e.end,this.end))}last(){return this.parts[this.parts.length-1]}length(){return this.parts.length}clone(e,t){let i=new n,r=JSON.parse(JSON.stringify(this.parts)),o=r.pop();for(let l of r)i.add(l);let s=t.substr.substring(0,e-o.start),a=s.length;return i.add({start:o.start,end:o.start+a,length:a,substr:s}),i}},Wa=n=>{mm(),n=si(n);let e="",t=[new or];for(let i=0;i0){l=l.sort((u,d)=>u.length()-d.length());for(let u of l)wm(u,t)||t.push(u);continue}if(i>0&&c.size==1&&!c.has("3")){e+=qa(t,!1);let u=new or,d=t[0];d&&u.add(d.last()),t=[u]}}return e+=qa(t,!0),e};var Ua=(n,e)=>{if(n)return n[e]},Ya=(n,e)=>{if(n){for(var t,i=e.split(".");(t=i.shift())&&(n=n[t]););return n}},sr=(n,e,t)=>{var i,r;return!n||(n=n+"",e.regex==null)||(r=n.search(e.regex),r===-1)?0:(i=e.string.length/n.length,r===0&&(i+=.5),i*t)},ar=(n,e)=>{var t=n[e];if(typeof t=="function")return t;t&&!Array.isArray(t)&&(n[e]=[t])},ai=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},Ga=(n,e)=>typeof n=="number"&&typeof e=="number"?n>e?1:ne?1:e>n?-1:0);var lr=class{constructor(e,t){se(this,"items");se(this,"settings");this.items=e,this.settings=t||{diacritics:!0}}tokenize(e,t,i){if(!e||!e.length)return[];let r=[],o=e.split(/\s+/);var s;return i&&(s=new RegExp("^("+Object.keys(i).map(Mt).join("|")+"):(.*)$")),o.forEach(a=>{let l,c=null,u=null;s&&(l=a.match(s))&&(c=l[1],a=l[2]),a.length>0&&(this.settings.diacritics?u=Wa(a)||null:u=Mt(a),u&&t&&(u="\\b"+u)),r.push({string:a,regex:u?new RegExp(u,"iu"):null,field:c})}),r}getScoreFunction(e,t){var i=this.prepareSearch(e,t);return this._getScoreFunction(i)}_getScoreFunction(e){let t=e.tokens,i=t.length;if(!i)return function(){return 0};let r=e.options.fields,o=e.weights,s=r.length,a=e.getAttrFn;if(!s)return function(){return 1};let l=(function(){return s===1?function(c,u){let d=r[0].field;return sr(a(u,d),c,o[d]||1)}:function(c,u){var d=0;if(c.field){let p=a(u,c.field);!c.regex&&p?d+=1/s:d+=sr(p,c,1)}else ai(o,(p,y)=>{d+=sr(a(u,y),c,p)});return d/s}})();return i===1?function(c){return l(t[0],c)}:e.options.conjunction==="and"?function(c){var u,d=0;for(let p of t){if(u=l(p,c),u<=0)return 0;d+=u}return d/i}:function(c){var u=0;return ai(t,d=>{u+=l(d,c)}),u/i}}getSortFunction(e,t){var i=this.prepareSearch(e,t);return this._getSortFunction(i)}_getSortFunction(e){var t,i=[];let r=this,o=e.options,s=!e.query&&o.sort_empty?o.sort_empty:o.sort;if(typeof s=="function")return s.bind(this);let a=function(c,u){return c==="$score"?u.score:e.getAttrFn(r.items[u.id],c)};if(s)for(let c of s)(e.query||c.field!=="$score")&&i.push(c);if(e.query){t=!0;for(let c of i)if(c.field==="$score"){t=!1;break}t&&i.unshift({field:"$score",direction:"desc"})}else i=i.filter(c=>c.field!=="$score");return i.length?function(c,u){var d,p;for(let y of i)if(p=y.field,d=(y.direction==="desc"?-1:1)*Ga(a(p,c),a(p,u)),d)return d;return 0}:null}prepareSearch(e,t){let i={};var r=Object.assign({},t);if(ar(r,"sort"),ar(r,"sort_empty"),r.fields){ar(r,"fields");let o=[];r.fields.forEach(s=>{typeof s=="string"&&(s={field:s,weight:1}),o.push(s),i[s.field]="weight"in s?s.weight:1}),r.fields=o}return{options:r,query:e.toLowerCase().trim(),tokens:this.tokenize(e,r.respect_word_boundaries,i),total:0,items:[],weights:i,getAttrFn:r.nesting?Ya:Ua}}search(e,t){var i=this,r,o;o=this.prepareSearch(e,t),t=o.options,e=o.query;let s=t.score||i._getScoreFunction(o);e.length?ai(i.items,(l,c)=>{r=s(l),(t.filter===!1||r>0)&&o.items.push({score:r,id:c})}):ai(i.items,(l,c)=>{o.items.push({score:1,id:c})});let a=i._getSortFunction(o);return a&&o.items.sort(a),o.total=o.items.length,typeof t.limit=="number"&&(o.items=o.items.slice(0,t.limit)),o}};var Fe=n=>typeof n=="undefined"||n===null?null:li(n),li=n=>typeof n=="boolean"?n?"1":"0":n+"",cr=n=>(n+"").replace(/&/g,"&").replace(//g,">").replace(/"/g,"""),Ka=(n,e)=>e>0?window.setTimeout(n,e):(n.call(null),null),Xa=(n,e)=>{var t;return function(i,r){var o=this;t&&(o.loading=Math.max(o.loading-1,0),clearTimeout(t)),t=setTimeout(function(){t=null,o.loadedSearches[i]=!0,n.call(o,i,r)},e)}},$o=(n,e,t)=>{var i,r=n.trigger,o={};n.trigger=function(){var s=arguments[0];if(e.indexOf(s)!==-1)o[s]=arguments;else return r.apply(n,arguments)},t.apply(n,[]),n.trigger=r;for(i of e)i in o&&r.apply(n,o[i])},Qa=n=>({start:n.selectionStart||0,length:(n.selectionEnd||0)-(n.selectionStart||0)}),fe=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},De=(n,e,t,i)=>{n.addEventListener(e,t,i)},Nt=(n,e)=>{if(!e||!e[n])return!1;var t=(e.altKey?1:0)+(e.ctrlKey?1:0)+(e.shiftKey?1:0)+(e.metaKey?1:0);return t===1},ur=(n,e)=>{let t=n.getAttribute("id");return t||(n.setAttribute("id",e),e)},Bo=n=>n.replace(/[\\"']/g,"\\$&"),kt=(n,e)=>{e&&n.append(e)},ve=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)};var nt=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Vo(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Vo=n=>typeof n=="string"&&n.indexOf("<")>-1,Ja=n=>n.replace(/['"\\]/g,"\\$&"),dr=(n,e)=>{var t=document.createEvent("HTMLEvents");t.initEvent(e,!0,!1),n.dispatchEvent(t)},ci=(n,e)=>{Object.assign(n.style,e)},$e=(n,...e)=>{var t=Za(e);n=el(n),n.map(i=>{t.map(r=>{i.classList.add(r)})})},gt=(n,...e)=>{var t=Za(e);n=el(n),n.map(i=>{t.map(r=>{i.classList.remove(r)})})},Za=n=>{var e=[];return ve(n,t=>{typeof t=="string"&&(t=t.trim().split(/[\t\n\f\r\s]/)),Array.isArray(t)&&(e=e.concat(t))}),e.filter(Boolean)},el=n=>(Array.isArray(n)||(n=[n]),n),fr=(n,e,t)=>{if(!(t&&!t.contains(n)))for(;n&&n.matches;){if(n.matches(e))return n;n=n.parentNode}},zo=(n,e=0)=>e>0?n[n.length-1]:n[0],tl=n=>Object.keys(n).length===0,qo=(n,e)=>{if(!n)return-1;e=e||n.nodeName;for(var t=0;n=n.previousElementSibling;)n.matches(e)&&t++;return t},le=(n,e)=>{ve(e,(t,i)=>{t==null?n.removeAttribute(i):n.setAttribute(i,""+t)})},ui=(n,e)=>{n.parentNode&&n.parentNode.replaceChild(e,n)};var nl=(n,e)=>{if(e===null)return;if(typeof e=="string"){if(!e.length)return;e=new RegExp(e,"i")}let t=o=>{var s=o.data.match(e);if(s&&o.data.length>0){var a=document.createElement("span");a.className="highlight";var l=o.splitText(s.index);l.splitText(s[0].length);var c=l.cloneNode(!0);return a.appendChild(c),ui(l,a),1}return 0},i=o=>{o.nodeType===1&&o.childNodes&&!/(script|style)/i.test(o.tagName)&&(o.className!=="highlight"||o.tagName!=="SPAN")&&Array.from(o.childNodes).forEach(s=>{r(s)})},r=o=>o.nodeType===3?t(o):(i(o),0);r(n)},il=n=>{var e=n.querySelectorAll("span.highlight");Array.prototype.forEach.call(e,function(t){var i=t.parentNode;i.replaceChild(t.firstChild,t),i.normalize()})};var xm=typeof navigator=="undefined"?!1:/Mac/.test(navigator.userAgent),di=xm?"metaKey":"ctrlKey";var jo={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:null,createOnBlur:!1,createFilter:null,clearAfterSelect:!1,highlight:!0,openOnFocus:!0,shouldOpen:null,maxOptions:50,maxItems:null,hideSelected:null,duplicates:!1,addPrecedence:!1,selectOnTab:!1,preload:null,allowEmptyOption:!1,refreshThrottle:300,loadThrottle:300,loadingClass:"loading",dataAttr:null,optgroupField:"optgroup",valueField:"value",labelField:"text",disabledField:"disabled",optgroupLabelField:"label",optgroupValueField:"value",lockOptgroupOrder:!1,sortField:"$order",searchField:["text"],searchConjunction:"and",mode:null,wrapperClass:"ts-wrapper",controlClass:"ts-control",dropdownClass:"ts-dropdown",dropdownContentClass:"ts-dropdown-content",itemClass:"item",optionClass:"option",dropdownParent:null,controlInput:'',copyClassesToDropdown:!1,placeholder:null,hidePlaceholder:null,shouldLoad:function(n){return n.length>0},render:{}};function hr(n,e){var t=Object.assign({},jo,e),i=t.dataAttr,r=t.labelField,o=t.valueField,s=t.disabledField,a=t.optgroupField,l=t.optgroupLabelField,c=t.optgroupValueField,u=n.tagName.toLowerCase(),d=n.getAttribute("placeholder")||n.getAttribute("data-placeholder");if(!d&&!t.allowEmptyOption){let g=n.querySelector('option[value=""]');g&&(d=g.textContent)}var p={placeholder:d,options:[],optgroups:[],items:[],maxItems:null},y=()=>{var g,_=p.options,x={},A=1;let w=0;var C=H=>{var I=Object.assign({},H.dataset),L=i&&I[i];return typeof L=="string"&&L.length&&(I=Object.assign(I,JSON.parse(L))),I},$=(H,I)=>{var L=Fe(H.value);if(L!=null&&!(!L&&!t.allowEmptyOption)){if(x.hasOwnProperty(L)){if(I){var j=x[L][a];j?Array.isArray(j)?j.push(I):x[L][a]=[j,I]:x[L][a]=I}}else{var G=C(H);G[r]=G[r]||H.textContent,G[o]=G[o]||L,G[s]=G[s]||H.disabled,G[a]=G[a]||I,G.$option=H,G.$order=G.$order||++w,x[L]=G,_.push(G)}H.selected&&p.items.push(L)}},q=H=>{var I,L;L=C(H),L[l]=L[l]||H.getAttribute("label")||"",L[c]=L[c]||A++,L[s]=L[s]||H.disabled,L.$order=L.$order||++w,p.optgroups.push(L),I=L[c],ve(H.children,j=>{$(j,I)})};p.maxItems=n.hasAttribute("multiple")?null:1,ve(n.children,H=>{g=H.tagName.toLowerCase(),g==="optgroup"?q(H):g==="option"&&$(H)})},m=()=>{var g,_;let x=n.getAttribute(i);if(x)p.options=JSON.parse(x),ve(p.options,w=>{p.items.push(w[o])});else{var A=(_=(g=n==null?void 0:n.value)===null||g===void 0?void 0:g.trim())!==null&&_!==void 0?_:"";if(!t.allowEmptyOption&&!A.length)return;let w=A.split(t.delimiter);ve(w,C=>{let $={};$[r]=C,$[o]=C,p.options.push($)}),p.items=w}};return u==="select"?y():m(),Object.assign({},jo,p,e)}var sl=0,we=class extends Ho(ri){constructor(e,t){super(),this.order=0,this.isOpen=!1,this.isDisabled=!1,this.isReadOnly=!1,this.isInvalid=!1,this.isValid=!0,this.isLocked=!1,this.isFocused=!1,this.isInputHidden=!1,this.isSetup=!1,this.isDropdownContentStale=!0,this.ignoreFocus=!1,this.ignoreHover=!1,this.hasOptions=!1,this.lastValue="",this.caretPos=0,this.loading=0,this.loadedSearches={},this.activeOption=null,this.activeItems=[],this.optgroups={},this.options={},this.userOptions={},this.items=[],this.refreshTimeout=null,sl++;var i,r=nt(e);if(r.tomselect)throw new Error("Tom Select already initialized on this element");r.tomselect=this;var o=window.getComputedStyle&&window.getComputedStyle(r,null);i=o.getPropertyValue("direction");let s=hr(r,t);this.settings=s,this.input=r,this.tabIndex=r.tabIndex||0,this.is_select_tag=r.tagName.toLowerCase()==="select",this.rtl=/rtl/i.test(i),this.inputId=ur(r,"tomselect-"+sl),this.isRequired=r.required,this.sifter=new lr(this.options,{diacritics:s.diacritics}),s.mode=s.mode||(s.maxItems===1?"single":"multi"),typeof s.hideSelected!="boolean"&&(s.hideSelected=s.mode==="multi"),typeof s.hidePlaceholder!="boolean"&&(s.hidePlaceholder=s.mode!=="multi");var a=s.createFilter;typeof a!="function"&&(typeof a=="string"&&(a=new RegExp(a)),a instanceof RegExp?s.createFilter=_=>a.test(_):s.createFilter=_=>this.settings.duplicates||!this.options[_]),this.initializePlugins(s.plugins),this.setupCallbacks(),this.setupTemplates();let l=nt("
"),c=nt("
"),u=this._render("dropdown"),d=nt('
'),p=this.input.getAttribute("class")||"",y=s.mode;var m;if($e(l,s.wrapperClass,p,y),$e(c,s.controlClass),kt(l,c),$e(u,s.dropdownClass,y),s.copyClassesToDropdown&&$e(u,p),$e(d,s.dropdownContentClass),kt(u,d),nt(s.dropdownParent||l).appendChild(u),Vo(s.controlInput)){m=nt(s.controlInput);var g=["autocorrect","autocapitalize","autocomplete","spellcheck","aria-label"];ve(g,_=>{r.getAttribute(_)&&le(m,{[_]:r.getAttribute(_)})}),m.tabIndex=-1,c.appendChild(m),this.focus_node=m}else s.controlInput?(m=nt(s.controlInput),this.focus_node=m):(m=nt(""),this.focus_node=c);this.wrapper=l,this.dropdown=u,this.dropdown_content=d,this.control=c,this.control_input=m,this.setup()}setup(){let e=this,t=e.settings,i=e.control_input,r=e.dropdown,o=e.dropdown_content,s=e.wrapper,a=e.control,l=e.input,c=e.focus_node,u={passive:!0},d=e.inputId+"-ts-dropdown";le(o,{id:d}),le(c,{role:"combobox","aria-haspopup":"listbox","aria-expanded":"false","aria-controls":d});let p=ur(c,e.inputId+"-ts-control"),y="label[for='"+Ja(e.inputId)+"']",m=document.querySelector(y),g=e.focus.bind(e);if(m){De(m,"click",g),le(m,{for:p});let w=ur(m,e.inputId+"-ts-label");le(c,{"aria-labelledby":w}),le(o,{"aria-labelledby":w})}if(s.style.width=l.style.width,s.style.minWidth=l.style.minWidth,s.style.maxWidth=l.style.maxWidth,e.plugins.names.length){let w="plugin-"+e.plugins.names.join(" plugin-");$e([s,r],w)}(t.maxItems===null||t.maxItems>1)&&e.is_select_tag&&le(l,{multiple:"multiple"}),t.placeholder&&le(i,{placeholder:t.placeholder}),!t.splitOn&&t.delimiter&&(t.splitOn=new RegExp("\\s*"+Mt(t.delimiter)+"+\\s*")),t.load&&t.loadThrottle&&(t.load=Xa(t.load,t.loadThrottle)),De(r,"mousemove",()=>{e.ignoreHover=!1}),De(r,"mouseenter",w=>{var C=fr(w.target,"[data-selectable]",r);C&&e.onOptionHover(w,C)},{capture:!0}),De(r,"click",w=>{let C=fr(w.target,"[data-selectable]");C&&(e.onOptionSelect(w,C),fe(w,!0))}),De(a,"click",w=>{var C=fr(w.target,"[data-ts-item]",a);if(C&&e.onItemSelect(w,C)){fe(w,!0);return}i.value==""&&(e.onClick(),fe(w,!0))}),De(c,"keydown",w=>e.onKeyDown(w)),De(i,"keypress",w=>e.onKeyPress(w)),De(i,"input",w=>e.onInput(w)),De(c,"blur",w=>e.onBlur(w)),De(c,"focus",w=>e.onFocus(w)),De(i,"paste",w=>e.onPaste(w));let _=w=>{let C=w.composedPath()[0];if(!s.contains(C)&&!r.contains(C)){e.isFocused&&e.blur(),e.inputState();return}C==i&&e.isOpen?w.stopPropagation():fe(w,!0)},x=()=>{e.isOpen&&e.positionDropdown()},A=()=>{e.isValid&&(e.isValid=!1,e.isInvalid=!0,e.refreshState())};De(l,"invalid",A),De(document,"mousedown",_),De(window,"scroll",x,u),De(window,"resize",x,u),this._destroy=()=>{l.removeEventListener("invalid",A),document.removeEventListener("mousedown",_),window.removeEventListener("scroll",x),window.removeEventListener("resize",x),m&&m.removeEventListener("click",g)},this.revertSettings={innerHTML:l.innerHTML,tabIndex:l.tabIndex},l.tabIndex=-1,l.insertAdjacentElement("afterend",e.wrapper),e.sync(!1),t.items=[],delete t.optgroups,delete t.options,e.refreshItems(),e.close(!1),e.inputState(),e.isSetup=!0,e.on("change",this.onChange),$e(l,"tomselected","ts-hidden-accessible"),e.trigger("initialize"),t.preload===!0&&e.preload()}setupOptions(e=[],t=[]){this.addOptions(e),ve(t,i=>{this.registerOptionGroup(i)})}setupTemplates(){var e=this,t=e.settings.labelField,i=e.settings.optgroupLabelField,r={optgroup:o=>{let s=document.createElement("div");return s.className="optgroup",s.appendChild(o.options),s},optgroup_header:(o,s)=>'
'+s(o[i])+"
",option:(o,s)=>"
"+s(o[t])+"
",item:(o,s)=>"
"+s(o[t])+"
",option_create:(o,s)=>'
Add '+s(o.input)+"
",no_results:()=>'
No results found
',loading:()=>'
',not_loading:()=>{},dropdown:()=>"
"};e.settings.render=Object.assign({},r,e.settings.render)}setupCallbacks(){var e,t,i={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",item_select:"onItemSelect",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(e in i)t=this.settings[i[e]],t&&this.on(e,t)}sync(e=!0){let t=this,i=e?hr(t.input,{delimiter:t.settings.delimiter,allowEmptyOption:t.settings.allowEmptyOption}):t.settings;t.setupOptions(i.options,i.optgroups),t.setValue(i.items||[],!0),t.input.disabled?t.disable():t.input.readOnly?t.setReadOnly(!0):t.enable(),t.lastQuery=null}onClick(){var e=this;if(e.activeItems.length>0){e.clearActiveItems(),e.focus();return}e.isFocused&&e.isOpen?e.blur():e.focus()}onMouseDown(){}onChange(){dr(this.input,"input"),dr(this.input,"change")}onPaste(e){var t=this;if(t.isInputHidden||t.isLocked){fe(e);return}t.settings.splitOn&&setTimeout(()=>{var i=t.inputValue();if(i.match(t.settings.splitOn)){var r=i.trim().split(t.settings.splitOn);ve(r,o=>{Fe(o)&&(this.options[o]?t.addItem(o):t.createItem(o))})}},0)}onKeyPress(e){var t=this;if(t.isLocked){fe(e);return}var i=String.fromCharCode(e.keyCode||e.which);if(t.settings.create&&t.settings.mode==="multi"&&i===t.settings.delimiter){t.createItem(),fe(e);return}}onKeyDown(e){var t=this;if(t.ignoreHover=!0,t.isLocked){e.keyCode!==9&&fe(e);return}switch(e.keyCode){case 65:if(Nt(di,e)&&t.control_input.value==""){fe(e),t.selectAll();return}break;case 27:t.isOpen&&(fe(e,!0),t.close()),t.clearActiveItems();return;case 40:if(!t.isOpen&&t.hasOptions)t.open();else if(t.activeOption){let i=t.getAdjacent(t.activeOption,1);i&&t.setActiveOption(i)}fe(e);return;case 38:if(t.activeOption){let i=t.getAdjacent(t.activeOption,-1);i&&t.setActiveOption(i)}fe(e);return;case 13:t.canSelect(t.activeOption)?(t.onOptionSelect(e,t.activeOption),fe(e)):t.settings.create&&t.createItem()?fe(e):document.activeElement==t.control_input&&t.isOpen&&fe(e);return;case 37:t.advanceSelection(-1,e);return;case 39:t.advanceSelection(1,e);return;case 9:t.settings.selectOnTab&&(t.canSelect(t.activeOption)?(t.onOptionSelect(e,t.activeOption),fe(e)):t.settings.create&&t.createItem()&&fe(e));return;case 8:case 46:t.deleteSelection(e);return}t.isInputHidden&&!Nt(di,e)&&fe(e)}onInput(e){if(this.isLocked)return;let t=this.inputValue();if(this.lastValue!==t){if(this.lastValue=t,t==""){this._onInput();return}this.refreshTimeout&&window.clearTimeout(this.refreshTimeout),this.refreshTimeout=Ka(()=>{this.refreshTimeout=null,this._onInput()},this.settings.refreshThrottle)}}_onInput(){let e=this.lastValue;this.settings.shouldLoad.call(this,e)&&this.load(e),this.refreshOptions(),this.trigger("type",e)}onOptionHover(e,t){this.ignoreHover||this.setActiveOption(t,!1)}onFocus(e){var t=this,i=t.isFocused;if(t.isDisabled||t.isReadOnly){t.blur(),fe(e);return}t.ignoreFocus||(t.isFocused=!0,t.settings.preload==="focus"&&t.preload(),i||t.trigger("focus"),t.activeItems.length||(t.inputState(),t.refreshOptions(!!t.settings.openOnFocus)),t.refreshState())}onBlur(e){if(document.hasFocus()!==!1){var t=this;if(t.isFocused){t.isFocused=!1,t.ignoreFocus=!1;var i=()=>{t.close(),t.setActiveItem(),t.setCaret(t.items.length),t.trigger("blur")};t.settings.create&&t.settings.createOnBlur?t.createItem(null,i):i()}}}onOptionSelect(e,t){var i,r=this;t.parentElement&&t.parentElement.matches("[data-disabled]")||(t.classList.contains("create")?r.createItem(null,()=>{r.settings.closeAfterSelect?r.close():r.settings.clearAfterSelect&&r.setTextboxValue()}):(i=t.dataset.value,typeof i!="undefined"&&(r.isDropdownContentStale=r.settings.hideSelected,r.addItem(i),r.settings.closeAfterSelect?r.close():r.settings.clearAfterSelect&&r.setTextboxValue(),!r.settings.hideSelected&&e.type&&/click/.test(e.type)&&r.setActiveOption(t))))}canSelect(e){return!!(this.isOpen&&e&&this.dropdown_content.contains(e))}onItemSelect(e,t){var i=this;return!i.isLocked&&i.settings.mode==="multi"?(fe(e),i.setActiveItem(t,e),!0):!1}canLoad(e){return!(!this.settings.load||this.loadedSearches.hasOwnProperty(e))}load(e){let t=this;if(!t.canLoad(e))return;$e(t.wrapper,t.settings.loadingClass),t.loading++;let i=t.loadCallback.bind(t);t.settings.load.call(t,e,i)}loadCallback(e,t){let i=this;i.loading=Math.max(i.loading-1,0),i.isDropdownContentStale=!0,i.clearActiveOption(),i.setupOptions(e,t),i.refreshOptions(i.isFocused&&!i.isInputHidden),i.loading||gt(i.wrapper,i.settings.loadingClass),i.trigger("load",e,t)}preload(){var e=this.wrapper.classList;e.contains("preloaded")||(e.add("preloaded"),this.load(""))}setTextboxValue(e=""){var t=this.control_input,i=t.value!==e;i&&(t.value=e,dr(t,"update"),this.lastValue=e)}getValue(){return this.is_select_tag&&this.input.hasAttribute("multiple")?this.items:this.items.join(this.settings.delimiter)}setValue(e,t){var i=t?[]:["change"];$o(this,i,()=>{this.clear(t),this.addItems(e,t)})}setMaxItems(e){e===0&&(e=null),this.settings.maxItems=e,this.refreshState()}setActiveItem(e,t){var i=this,r,o,s,a,l,c;if(i.settings.mode!=="single"){if(!e){i.clearActiveItems(),i.isFocused&&i.inputState();return}if(r=t&&t.type.toLowerCase(),r==="click"&&Nt("shiftKey",t)&&i.activeItems.length){for(c=i.getLastActive(),s=Array.prototype.indexOf.call(i.control.children,c),a=Array.prototype.indexOf.call(i.control.children,e),s>a&&(l=s,s=a,a=l),o=s;o<=a;o++)e=i.control.children[o],i.activeItems.indexOf(e)===-1&&i.setActiveItemClass(e);fe(t)}else r==="click"&&Nt(di,t)||r==="keydown"&&Nt("shiftKey",t)?e.classList.contains("active")?i.removeActiveItem(e):i.setActiveItemClass(e):(i.clearActiveItems(),i.setActiveItemClass(e));i.inputState(),i.isFocused||i.focus()}}setActiveItemClass(e){let t=this,i=t.control.querySelector(".last-active");i&>(i,"last-active"),$e(e,"active last-active"),t.trigger("item_select",e),t.activeItems.indexOf(e)==-1&&t.activeItems.push(e)}removeActiveItem(e){var t=this.activeItems.indexOf(e);this.activeItems.splice(t,1),gt(e,"active")}clearActiveItems(){gt(this.activeItems,"active"),this.activeItems=[]}setActiveOption(e,t=!0){e!==this.activeOption&&(this.clearActiveOption(),e&&(this.activeOption=e,le(this.focus_node,{"aria-activedescendant":e.getAttribute("id")}),le(e,{"aria-selected":"true"}),$e(e,"active"),t&&this.scrollToOption(e)))}scrollToOption(e,t){if(!e)return;let i=this.dropdown_content,r=i.clientHeight,o=i.scrollTop||0,s=e.offsetHeight,a=e.getBoundingClientRect().top-i.getBoundingClientRect().top+o;a+s>r+o?this.scroll(a-r+s,t):a{e.setActiveItemClass(i)}))}inputState(){var e=this;e.control.contains(e.control_input)&&(le(e.control_input,{placeholder:e.settings.placeholder}),e.activeItems.length>0||!e.isFocused&&e.settings.hidePlaceholder&&e.items.length>0?(e.setTextboxValue(),e.isInputHidden=!0):(e.settings.hidePlaceholder&&e.items.length>0&&le(e.control_input,{placeholder:""}),e.isInputHidden=!1),e.wrapper.classList.toggle("input-hidden",e.isInputHidden))}inputValue(){return this.control_input.value.trim()}focus(){var e=this;if(e.isDisabled||e.isReadOnly)return;e.ignoreFocus=!0;let t=this.control_input.offsetWidth?this.control_input:this.focus_node;t.focus(),setTimeout(()=>{e.ignoreFocus=!1,t.getRootNode().activeElement===t&&this.onFocus()},0)}blur(){this.focus_node.blur(),this.onBlur()}getScoreFunction(e){return this.sifter.getScoreFunction(e,this.getSearchOptions())}getSearchOptions(){var e=this.settings,t=e.sortField;return typeof e.sortField=="string"&&(t=[{field:e.sortField}]),{fields:e.searchField,conjunction:e.searchConjunction,sort:t,nesting:e.nesting}}search(e){var t,i,r=this,o=this.getSearchOptions();if(r.settings.score&&(i=r.settings.score.call(r,e),typeof i!="function"))throw new Error('Tom Select "score" setting must be a function that returns a function');return r.isDropdownContentStale||e!==r.lastQuery?(r.lastQuery=e,/(.)\1{15,}/.test(e)&&(e=""),t=r.sifter.search(e,Object.assign(o,{score:i})),r.currentResults=t):t=Object.assign({},r.currentResults),r.settings.hideSelected&&(t.items=t.items.filter(s=>{let a=Fe(s.id);return!(a!==null&&r.items.indexOf(a)!==-1)})),t}refreshOptions(e=!0){var t,i,r,o,s,a,l,c,u,d;let p={},y=[];var m=this,g=m.inputValue();let _=g===m.lastQuery||g==""&&m.lastQuery==null;var x=m.search(g),A=null,w=m.settings.shouldOpen||!1,C=m.dropdown_content;_&&(A=m.activeOption,A&&(u=A.closest("[data-group]"))),o=x.items.length,typeof m.settings.maxOptions=="number"&&(o=Math.min(o,m.settings.maxOptions)),o>0&&(w=!0);let $=(I,L)=>{let j=p[I];if(j!==void 0){let Q=y[j];if(Q!==void 0)return[j,Q.fragment]}let G=document.createDocumentFragment();return j=y.length,y.push({fragment:G,order:L,optgroup:I}),[j,G]};for(t=0;t0&&(Q=Q.cloneNode(!0),le(Q,{id:j.$id+"-clone-"+i,"aria-selected":null}),Q.classList.add("ts-cloned"),gt(Q,"active"),m.activeOption&&m.activeOption.dataset.value==L&&u&&u.dataset.group===s.toString()&&(A=Q)),ie.appendChild(Q),s!=""&&(p[s]=Ce)}}m.settings.lockOptgroupOrder&&y.sort((I,L)=>I.order-L.order),l=document.createDocumentFragment(),ve(y,I=>{let L=I.fragment,j=I.optgroup;if(!L||!L.children.length)return;let G=m.optgroups[j];if(G!==void 0){let Q=document.createDocumentFragment(),Z=m.render("optgroup_header",G);kt(Q,Z),kt(Q,L);let he=m.render("optgroup",{group:G,options:Q});kt(l,he)}else kt(l,L)}),C.innerHTML="",kt(C,l),m.isDropdownContentStale=!1,m.settings.highlight&&(il(C),x.query.length&&x.tokens.length&&ve(x.tokens,I=>{nl(C,I.regex)}));var H=I=>{let L=m.render(I,{input:g});return L&&(w=!0,C.insertBefore(L,C.firstChild)),L};if(m.loading?H("loading"):m.settings.shouldLoad.call(m,g)?x.items.length===0&&H("no_results"):H("not_loading"),c=m.canCreate(g),c&&(d=H("option_create")),m.hasOptions=x.items.length>0||c,w){if(x.items.length>0){if(!A&&m.settings.mode==="single"&&m.items[0]!=null&&(A=m.getOption(m.items[0])),!C.contains(A)){let I=0;d&&!m.settings.addPrecedence&&(I=1),A=m.selectable()[I]}}else d&&(A=d);e&&!m.isOpen&&(m.open(),m.scrollToOption(A,"auto")),m.setActiveOption(A)}else m.clearActiveOption(),e&&m.isOpen&&m.close(!1)}selectable(){return this.dropdown_content.querySelectorAll("[data-selectable]")}addOption(e,t=!1){let i=this;if(Array.isArray(e))return i.addOptions(e,t),!1;let r=Fe(e[i.settings.valueField]);return r===null||i.options.hasOwnProperty(r)?(i.updateOption(e[i.settings.valueField],e),!1):(e.$order=e.$order||++i.order,e.$id=i.inputId+"-opt-"+e.$order,i.options[r]=e,i.isDropdownContentStale=!0,t&&(i.userOptions[r]=t,i.trigger("option_add",r,e)),r)}addOptions(e,t=!1){ve(e,i=>{this.addOption(i,t)})}registerOption(e){return this.addOption(e)}registerOptionGroup(e){var t=Fe(e[this.settings.optgroupValueField]);return t===null?!1:(e.$order=e.$order||++this.order,this.optgroups[t]=e,t)}addOptionGroup(e,t){var i;t[this.settings.optgroupValueField]=e,(i=this.registerOptionGroup(t))&&this.trigger("optgroup_add",i,t)}removeOptionGroup(e){this.optgroups.hasOwnProperty(e)&&(delete this.optgroups[e],this.clearCache(),this.trigger("optgroup_remove",e))}clearOptionGroups(){this.optgroups={},this.clearCache(),this.trigger("optgroup_clear")}updateOption(e,t){let i=this;var r,o;let s=Fe(e),a=Fe(t[i.settings.valueField]);if(s===null)return;let l=i.options[s];if(l==null)return;if(typeof a!="string")throw new Error("Value must be set in option data");let c=i.getOption(s),u=i.getItem(s);if(t.$order=t.$order||l.$order,delete i.options[s],i.uncacheValue(a),i.options[a]=t,c){if(i.dropdown_content.contains(c)){let d=i._render("option",t);ui(c,d),i.activeOption===c&&i.setActiveOption(d)}c.remove()}u&&(o=i.items.indexOf(s),o!==-1&&i.items.splice(o,1,a),r=i._render("item",t),u.classList.contains("active")&&$e(r,"active"),ui(u,r)),i.isDropdownContentStale=!0}removeOption(e,t){let i=this;e=li(e),i.uncacheValue(e),delete i.userOptions[e],delete i.options[e],i.isDropdownContentStale=!0,i.trigger("option_remove",e),i.removeItem(e,t)}clearOptions(e){let t=(e||this.clearFilter).bind(this);this.loadedSearches={},this.userOptions={},this.clearCache();let i={};ve(this.options,(r,o)=>{t(r,o)&&(i[o]=r)}),this.options=this.sifter.items=i,this.isDropdownContentStale=!0,this.trigger("option_clear")}clearFilter(e,t){return this.items.indexOf(t)>=0}getOption(e,t=!1){let i=Fe(e);if(i===null)return null;let r=this.options[i];if(r!=null){if(r.$div)return r.$div;if(t)return this._render("option",r)}return null}getAdjacent(e,t,i="option"){var r=this,o;if(!e)return null;i=="item"?o=r.controlChildren():o=r.dropdown_content.querySelectorAll("[data-selectable]");for(let s=0;s0?o[s+1]:o[s-1];return null}getItem(e){if(typeof e=="object")return e;var t=Fe(e);return t!==null?this.control.querySelector(`[data-value="${Bo(t)}"]`):null}addItems(e,t){var i=this,r=Array.isArray(e)?e:[e];r=r.filter(s=>i.items.indexOf(s)===-1);let o=r[r.length-1];r.forEach(s=>{i.isPending=s!==o,i.addItem(s,t)})}addItem(e,t){var i=t?[]:["change","dropdown_close"];$o(this,i,()=>{var r,o;let s=this,a=s.settings.mode,l=Fe(e);if(!(l&&s.items.indexOf(l)!==-1&&(a==="single"&&s.close(),a==="single"||!s.settings.duplicates))&&!(l===null||!s.options.hasOwnProperty(l))&&(a==="single"&&s.clear(t),!(a==="multi"&&s.isFull()))){if(r=s._render("item",s.options[l]),s.control.contains(r)&&(r=r.cloneNode(!0)),o=s.isFull(),s.items.splice(s.caretPos,0,l),s.insertAtCaret(r),s.isSetup){if(!s.isPending&&s.settings.hideSelected){let c=s.getOption(l),u=s.getAdjacent(c,1);u&&s.setActiveOption(u)}s.settings.clearAfterSelect&&s.setTextboxValue(),!s.isPending&&!s.settings.closeAfterSelect&&s.refreshOptions(s.isFocused&&a!=="single"),s.settings.closeAfterSelect!=!1&&s.isFull()?s.close():s.isPending||s.positionDropdown(),s.trigger("item_add",l,r),s.isPending||s.updateOriginalInput({silent:t})}(!s.isPending||!o&&s.isFull())&&(s.inputState(),s.refreshState())}})}removeItem(e=null,t){let i=this;if(e=i.getItem(e),!e)return;var r,o;let s=e.dataset.value;r=qo(e),e.remove(),e.classList.contains("active")&&(o=i.activeItems.indexOf(e),i.activeItems.splice(o,1),gt(e,"active")),i.items.splice(r,1),i.isDropdownContentStale=!0,!i.settings.persist&&i.userOptions.hasOwnProperty(s)&&i.removeOption(s,t),r{}){arguments.length===3&&(t=arguments[2]),typeof t!="function"&&(t=()=>{});var i=this,r=i.caretPos,o;if(e=e||i.inputValue(),!i.canCreate(e))return Fe(e)&&this.options[e]&&i.addItem(e),t(),!1;i.lock();var s=!1,a=l=>{if(i.unlock(),!l||typeof l!="object")return t();var c=Fe(l[i.settings.valueField]);if(typeof c!="string")return t();i.setTextboxValue(),i.addOption(l,!0),i.setCaret(r),i.addItem(c),t(l),s=!0};return typeof i.settings.create=="function"?o=i.settings.create.call(this,e,a):o={[i.settings.labelField]:e,[i.settings.valueField]:e},s||a(o),!0}refreshItems(){var e=this;e.isDropdownContentStale=!0,e.isSetup&&e.addItems(e.items),e.updateOriginalInput(),e.refreshState()}refreshState(){let e=this;e.refreshValidityState();let t=e.isFull(),i=e.isLocked;e.wrapper.classList.toggle("rtl",e.rtl);let r=e.wrapper.classList;r.toggle("focus",e.isFocused),r.toggle("disabled",e.isDisabled),r.toggle("readonly",e.isReadOnly),r.toggle("required",e.isRequired),r.toggle("invalid",!e.isValid),r.toggle("locked",i),r.toggle("full",t),r.toggle("input-active",e.isFocused&&!e.isInputHidden),r.toggle("dropdown-active",e.isOpen),r.toggle("has-options",tl(e.options)),r.toggle("has-items",e.items.length>0)}refreshValidityState(){var e=this;e.input.validity&&(e.isValid=e.input.validity.valid,e.isInvalid=!e.isValid)}isFull(){return this.settings.maxItems!==null&&this.items.length>=this.settings.maxItems}updateOriginalInput(e={}){let t=this;var i,r;let o=t.input.querySelector('option[value=""]');if(t.is_select_tag){let l=function(c,u,d){return c||(c=nt('")),c!=o&&t.input.append(c),s.push(c),(c!=o||a>0||t.settings.mode=="multi")&&(c.selected=!0),c},s=[],a=t.input.querySelectorAll("option:checked").length;t.input.querySelectorAll("option:checked").forEach(c=>{c.selected=!1}),t.items.length==0&&t.settings.mode=="single"?l(o,"",""):t.items.forEach(c=>{if(i=t.options[c],r=i[t.settings.labelField]||"",s.includes(i.$option)){let u=t.input.querySelector(`option[value="${Bo(c)}"]:not(:checked)`);l(u,c,r)}else i.$option=l(i.$option,c,r)})}else t.input.value=t.getValue();t.isSetup&&(e.silent||t.trigger("change",t.getValue()))}open(){var e=this;e.isLocked||e.isOpen||e.settings.mode==="multi"&&e.isFull()||(e.isOpen=!0,le(e.focus_node,{"aria-expanded":"true"}),e.refreshState(),ci(e.dropdown,{visibility:"hidden",display:"block"}),e.positionDropdown(),ci(e.dropdown,{visibility:"visible",display:"block"}),e.focus(),e.trigger("dropdown_open",e.dropdown))}close(e=!0){var t=this,i=t.isOpen;e&&(t.setTextboxValue(),t.settings.mode==="single"&&t.items.length&&t.inputState()),t.isOpen=!1,le(t.focus_node,{"aria-expanded":"false"}),ci(t.dropdown,{display:"none"}),t.settings.hideSelected&&t.clearActiveOption(),t.refreshState(),i&&t.trigger("dropdown_close",t.dropdown)}positionDropdown(){if(this.settings.dropdownParent==="body"){var e=this.control,t=e.getBoundingClientRect(),i=e.offsetHeight+t.top+window.scrollY,r=t.left+window.scrollX;ci(this.dropdown,{width:t.width+"px",top:i+"px",left:r+"px"})}}clear(e){var t=this;if(t.items.length){var i=t.controlChildren();ve(i,r=>{t.removeItem(r,!0)}),t.inputState(),e||t.updateOriginalInput(),t.trigger("clear")}}insertAtCaret(e){let t=this,i=t.caretPos,r=t.control;r.insertBefore(e,r.children[i]||null),t.setCaret(i+1)}deleteSelection(e){var t,i,r,o,s=this;t=e&&e.keyCode===8?-1:1,i=Qa(s.control_input);let a=[];if(s.activeItems.length)o=zo(s.activeItems,t),r=qo(o),t>0&&r++,ve(s.activeItems,l=>a.push(l));else if((s.isFocused||s.settings.mode==="single")&&s.items.length){let l=s.controlChildren(),c;t<0&&i.start===0&&i.length===0?c=l[s.caretPos-1]:t>0&&i.start===s.inputValue().length&&(c=l[s.caretPos]),c!==void 0&&a.push(c)}if(!s.shouldDelete(a,e))return!1;for(fe(e,!0),typeof r!="undefined"&&s.setCaret(r);a.length;)s.removeItem(a.pop());return s.inputState(),s.positionDropdown(),s.refreshOptions(!1),!0}shouldDelete(e,t){let i=e.map(r=>r.dataset.value);return!(!i.length||typeof this.settings.onDelete=="function"&&this.settings.onDelete.call(this,i,t)===!1)}advanceSelection(e,t){var i,r,o=this;o.rtl&&(e*=-1),!o.inputValue().length&&(Nt(di,t)||Nt("shiftKey",t)?(i=o.getLastActive(e),i?i.classList.contains("active")?r=o.getAdjacent(i,e,"item"):r=i:e>0?r=o.control_input.nextElementSibling:r=o.control_input.previousElementSibling,r&&(r.classList.contains("active")&&o.removeActiveItem(i),o.setActiveItemClass(r))):o.moveCaret(e))}moveCaret(e){}getLastActive(e){let t=this.control.querySelector(".last-active");if(t)return t;var i=this.control.querySelectorAll(".active");if(i)return zo(i,e)}setCaret(e){this.caretPos=this.items.length}controlChildren(){return Array.from(this.control.querySelectorAll("[data-ts-item]"))}lock(){this.setLocked(!0)}unlock(){this.setLocked(!1)}setLocked(e=this.isReadOnly||this.isDisabled){this.isLocked=e,this.refreshState()}disable(){this.setDisabled(!0),this.close()}enable(){this.setDisabled(!1)}setDisabled(e){this.focus_node.tabIndex=e?-1:this.tabIndex,this.isDisabled=e,this.input.disabled=e,this.control_input.disabled=e,this.setLocked()}setReadOnly(e){this.isReadOnly=e,this.input.readOnly=e,this.control_input.readOnly=e,this.setLocked()}destroy(){var e=this,t=e.revertSettings;e.trigger("destroy"),e.off(),e.wrapper.remove(),e.dropdown.remove(),e.input.innerHTML=t.innerHTML,e.input.tabIndex=t.tabIndex,gt(e.input,"tomselected","ts-hidden-accessible"),e._destroy(),delete e.input.tomselect}render(e,t){var i,r;let o=this;if(typeof this.settings.render[e]!="function"||(r=o.settings.render[e].call(this,t,cr),!r))return null;if(r=nt(r),e==="option"||e==="option_create"?t[o.settings.disabledField]?le(r,{"aria-disabled":"true"}):le(r,{"data-selectable":""}):e==="optgroup"&&(i=t.group[o.settings.optgroupValueField],le(r,{"data-group":i}),t.group[o.settings.disabledField]&&le(r,{"data-disabled":""})),e==="option"||e==="item"){let s=li(t[o.settings.valueField]);le(r,{"data-value":s}),e==="item"?($e(r,o.settings.itemClass),le(r,{"data-ts-item":""})):($e(r,o.settings.optionClass),le(r,{role:"option",id:t.$id}),t.$div=r,o.options[s]=t)}return r}_render(e,t){let i=this.render(e,t);if(i==null)throw"HTMLElement expected";return i}clearCache(){ve(this.options,e=>{e.$div&&(e.$div.remove(),delete e.$div)})}uncacheValue(e){let t=this.getOption(e);t&&t.remove()}canCreate(e){return this.settings.create&&e.length>0&&this.settings.createFilter.call(this,e)}hook(e,t,i){var r=this,o=r[t];r[t]=function(){var s,a;return e==="after"&&(s=o.apply(r,arguments)),a=i.apply(r,arguments),e==="instead"?a:(e==="before"&&(s=o.apply(r,arguments)),s)}}};var km=(n,e,t,i)=>{n.addEventListener(e,t,i)};function al(){km(this.input,"change",()=>{this.sync()})}var Hm=n=>typeof n=="undefined"||n===null?null:Im(n),Im=n=>typeof n=="boolean"?n?"1":"0":n+"",ll=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},Rm=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Pm(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Pm=n=>typeof n=="string"&&n.indexOf("<")>-1;function cl(n){var e=this,t=e.onOptionSelect;e.settings.hideSelected=!1;let i=Object.assign({className:"tomselect-checkbox",checkedClassNames:void 0,uncheckedClassNames:void 0},n);var r=function(a,l){l?(a.checked=!0,i.uncheckedClassNames&&a.classList.remove(...i.uncheckedClassNames),i.checkedClassNames&&a.classList.add(...i.checkedClassNames)):(a.checked=!1,i.checkedClassNames&&a.classList.remove(...i.checkedClassNames),i.uncheckedClassNames&&a.classList.add(...i.uncheckedClassNames))},o=function(a){setTimeout(()=>{var l=a.querySelector("input."+i.className);l instanceof HTMLInputElement&&r(l,a.classList.contains("selected"))},1)};e.hook("after","setupTemplates",()=>{var s=e.settings.render.option;e.settings.render.option=(a,l)=>{var c=Rm(s.call(e,a,l)),u=document.createElement("input");i.className&&u.classList.add(i.className),u.addEventListener("click",function(p){ll(p)}),u.type="checkbox";let d=Hm(a[e.settings.valueField]);return r(u,!!(d&&e.items.indexOf(d)>-1)),c.prepend(u),c}}),e.on("item_remove",s=>{var a=e.getOption(s);a&&(a.classList.remove("selected"),o(a))}),e.on("item_add",s=>{var a=e.getOption(s);a&&o(a)}),e.hook("instead","onOptionSelect",(s,a)=>{if(a.classList.contains("selected")){a.classList.remove("selected"),e.removeItem(a.dataset.value),e.refreshOptions(),ll(s,!0);return}t.call(e,s,a),o(a)})}var Fm=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if($m(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},$m=n=>typeof n=="string"&&n.indexOf("<")>-1;function ul(n){let e=this,t=Object.assign({className:"clear-button",title:"Clear All",role:"button",tabindex:0,html:i=>`
×
`},n);e.on("initialize",()=>{var i=Fm(t.html(t));i.addEventListener("click",r=>{e.isLocked||(e.clear(),e.settings.mode==="single"&&e.settings.allowEmptyOption&&e.addItem(""),e.refreshOptions(!1),r.preventDefault(),r.stopPropagation())}),e.control.appendChild(i)})}var Bm=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},On=(n,e,t,i)=>{n.addEventListener(e,t,i)},Vm=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},zm=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(qm(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},qm=n=>typeof n=="string"&&n.indexOf("<")>-1,jm=(n,e)=>{Vm(e,(t,i)=>{t==null?n.removeAttribute(i):n.setAttribute(i,""+t)})},Wm=(n,e)=>{var t;(t=n.parentNode)==null||t.insertBefore(e,n.nextSibling)},Um=(n,e)=>{var t;(t=n.parentNode)==null||t.insertBefore(e,n)},Ym=(n,e)=>{do{var t;if(e=(t=e)==null?void 0:t.previousElementSibling,n==e)return!0}while(e&&e.previousElementSibling);return!1};function dl(){var n=this;if(n.settings.mode!=="multi")return;var e=n.lock,t=n.unlock;let i=!0,r;n.hook("after","setupTemplates",()=>{var o=n.settings.render.item;n.settings.render.item=(s,a)=>{let l=zm(o.call(n,s,a));jm(l,{draggable:"true"});let c=g=>{i||Bm(g),g.stopPropagation()},u=g=>{r=l,setTimeout(()=>{l.classList.add("ts-dragging")},0)},d=g=>{g.preventDefault(),l.classList.add("ts-drag-over"),y(l,r)},p=()=>{l.classList.remove("ts-drag-over")},y=(g,_)=>{_!==void 0&&(Ym(_,l)?Wm(g,_):Um(g,_))},m=()=>{var g;document.querySelectorAll(".ts-drag-over").forEach(x=>x.classList.remove("ts-drag-over")),(g=r)==null||g.classList.remove("ts-dragging"),r=void 0;var _=[];n.control.querySelectorAll("[data-value]").forEach(x=>{if(x.dataset.value){let A=x.dataset.value;A&&_.push(A)}}),n.setValue(_)};return On(l,"mousedown",c),On(l,"dragstart",u),On(l,"dragenter",d),On(l,"dragover",d),On(l,"dragleave",p),On(l,"dragend",m),l}}),n.hook("instead","lock",()=>(i=!1,e.call(n))),n.hook("instead","unlock",()=>(i=!0,t.call(n)))}var Gm=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},Km=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Xm(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Xm=n=>typeof n=="string"&&n.indexOf("<")>-1;function fl(n){let e=this,t=Object.assign({title:"Untitled",headerClass:"dropdown-header",titleRowClass:"dropdown-header-title",labelClass:"dropdown-header-label",closeClass:"dropdown-header-close",html:i=>'
'+i.title+'×
'},n);e.on("initialize",()=>{var i=Km(t.html(t)),r=i.querySelector("."+t.closeClass);r&&r.addEventListener("click",o=>{Gm(o,!0),e.close()}),e.dropdown.insertBefore(i,e.dropdown.firstChild)})}var Qm=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},Jm=(n,...e)=>{var t=Zm(e);n=eg(n),n.map(i=>{t.map(r=>{i.classList.remove(r)})})},Zm=n=>{var e=[];return Qm(n,t=>{typeof t=="string"&&(t=t.trim().split(/[\t\n\f\r\s]/)),Array.isArray(t)&&(e=e.concat(t))}),e.filter(Boolean)},eg=n=>(Array.isArray(n)||(n=[n]),n),tg=(n,e)=>{if(!n)return-1;e=e||n.nodeName;for(var t=0;n=n.previousElementSibling;)n.matches(e)&&t++;return t};function hl(){var n=this;n.hook("instead","setCaret",e=>{n.settings.mode==="single"||!n.control.contains(n.control_input)?e=n.items.length:(e=Math.max(0,Math.min(n.items.length,e)),e!=n.caretPos&&!n.isPending&&n.controlChildren().forEach((t,i)=>{i{if(!n.isFocused)return;let t=n.getLastActive(e);if(t){let i=tg(t);n.setCaret(e>0?i+1:i),n.setActiveItem(),Jm(t,"last-active")}else n.setCaret(n.caretPos+e)})}var ng=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},ig=(n,e,t,i)=>{n.addEventListener(e,t,i)},rg=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},pl=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(og(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},og=n=>typeof n=="string"&&n.indexOf("<")>-1,sg=(n,...e)=>{var t=ag(e);n=lg(n),n.map(i=>{t.map(r=>{i.classList.add(r)})})},ag=n=>{var e=[];return rg(n,t=>{typeof t=="string"&&(t=t.trim().split(/[\t\n\f\r\s]/)),Array.isArray(t)&&(e=e.concat(t))}),e.filter(Boolean)},lg=n=>(Array.isArray(n)||(n=[n]),n);function ml(){let n=this;n.settings.shouldOpen=!0,n.hook("before","setup",()=>{var e;n.focus_node=n.control,sg(n.control_input,"dropdown-input");let t=pl('