From 3b76e0203a349be3318979e1266c34b3435f648b Mon Sep 17 00:00:00 2001 From: hSaria <34197532+hSaria@users.noreply.github.com> Date: Wed, 11 Dec 2019 07:03:39 +0000 Subject: [PATCH 01/59] Fixes 3749 attribute error --- netbox/users/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/users/views.py b/netbox/users/views.py index 47d3503d7..6a2410274 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -96,7 +96,7 @@ class ChangePasswordView(LoginRequiredMixin, View): def get(self, request): # LDAP users cannot change their password here - if getattr(request.user, 'ldap_username'): + if getattr(request.user, 'ldap_username', None): messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.") return redirect('user:profile') From b57d64c72db7987168d7f971bd9c8a2c95202b92 Mon Sep 17 00:00:00 2001 From: hSaria <34197532+hSaria@users.noreply.github.com> Date: Wed, 11 Dec 2019 07:11:59 +0000 Subject: [PATCH 02/59] Changelog for #3751 --- docs/release-notes/version-2.6.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index f499ec1ca..59d34495f 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -1,3 +1,9 @@ +# v2.6.9 (FUTURE) + +## Bug Fixes + +* [#3749](https://github.com/netbox-community/netbox/issues/3749) - Fix exception on password change page for local users + # v2.6.8 (2019-12-10) ## Enhancements From 77e0564d13a72f13363a5188489b9a04829399b4 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 13 Dec 2019 10:12:46 -0500 Subject: [PATCH 03/59] Closes #3152: Include direct link to rack elevations on site view --- docs/release-notes/version-2.6.md | 4 +++ netbox/templates/dcim/site.html | 41 +++++++++++++++++-------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 59d34495f..974a3e39d 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -1,5 +1,9 @@ # v2.6.9 (FUTURE) +## Enhancements + +* [#3152](https://github.com/netbox-community/netbox/issues/3152) - Include direct link to rack elevations on site view + ## Bug Fixes * [#3749](https://github.com/netbox-community/netbox/issues/3749) - Fix exception on password change page for local users diff --git a/netbox/templates/dcim/site.html b/netbox/templates/dcim/site.html index 0e38d2967..10e951efe 100644 --- a/netbox/templates/dcim/site.html +++ b/netbox/templates/dcim/site.html @@ -251,25 +251,28 @@
Rack Groups
- {% if rack_groups %} - - {% for rg in rack_groups %} - - - - - - {% endfor %} -
{{ rg }}{{ rg.rack_count }} - - - -
- {% else %} -
- None -
- {% endif %} + + {% for rg in rack_groups %} + + + + + + {% endfor %} + + + + + +
{{ rg }}{{ rg.rack_count }} + + + +
All racks{{ stats.rack_count }} + + + +
From 85c11bbd839e1e27f3b9bc99c653a902c6e42095 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 13 Dec 2019 10:37:58 -0500 Subject: [PATCH 04/59] Closes #3441: Move virtual machine results near devices in global search --- docs/release-notes/version-2.6.md | 1 + netbox/netbox/views.py | 34 +++++++++++++++---------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 974a3e39d..005a8f7c0 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -3,6 +3,7 @@ ## Enhancements * [#3152](https://github.com/netbox-community/netbox/issues/3152) - Include direct link to rack elevations on site view +* [#3441](https://github.com/netbox-community/netbox/issues/3441) - Move virtual machine results near devices in global search ## Bug Fixes diff --git a/netbox/netbox/views.py b/netbox/netbox/views.py index 05036a37a..5dee6cade 100644 --- a/netbox/netbox/views.py +++ b/netbox/netbox/views.py @@ -116,6 +116,23 @@ SEARCH_TYPES = OrderedDict(( 'table': PowerFeedTable, 'url': 'dcim:powerfeed_list', }), + # Virtualization + ('cluster', { + 'permission': 'virtualization.view_cluster', + 'queryset': Cluster.objects.prefetch_related('type', 'group'), + 'filter': ClusterFilter, + 'table': ClusterTable, + 'url': 'virtualization:cluster_list', + }), + ('virtualmachine', { + 'permission': 'virtualization.view_virtualmachine', + 'queryset': VirtualMachine.objects.prefetch_related( + 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', + ), + 'filter': VirtualMachineFilter, + 'table': VirtualMachineDetailTable, + 'url': 'virtualization:virtualmachine_list', + }), # IPAM ('vrf', { 'permission': 'ipam.view_vrf', @@ -168,23 +185,6 @@ SEARCH_TYPES = OrderedDict(( 'table': TenantTable, 'url': 'tenancy:tenant_list', }), - # Virtualization - ('cluster', { - 'permission': 'virtualization.view_cluster', - 'queryset': Cluster.objects.prefetch_related('type', 'group'), - 'filter': ClusterFilter, - 'table': ClusterTable, - 'url': 'virtualization:cluster_list', - }), - ('virtualmachine', { - 'permission': 'virtualization.view_virtualmachine', - 'queryset': VirtualMachine.objects.prefetch_related( - 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', - ), - 'filter': VirtualMachineFilter, - 'table': VirtualMachineDetailTable, - 'url': 'virtualization:virtualmachine_list', - }), )) From 462cede8631e45630482d7332a0faa9c274253f8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 13 Dec 2019 11:36:31 -0500 Subject: [PATCH 05/59] Fixes #2170: Prevent the deletion of a virtual chassis when a cross-member LAG is present --- docs/release-notes/version-2.6.md | 1 + netbox/dcim/models.py | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 005a8f7c0..5a64ceb9e 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -7,6 +7,7 @@ ## Bug Fixes +* [#2170](https://github.com/netbox-community/netbox/issues/2170) - Prevent the deletion of a virtual chassis when a cross-member LAG is present * [#3749](https://github.com/netbox-community/netbox/issues/3749) - Fix exception on password change page for local users # v2.6.8 (2019-12-10) diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index ad2a3d769..db88901b6 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -9,7 +9,7 @@ from django.contrib.postgres.fields import ArrayField, JSONField from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models -from django.db.models import Count, Q, Sum +from django.db.models import Count, F, ProtectedError, Q, Sum from django.urls import reverse from mptt.models import MPTTModel, TreeForeignKey from taggit.managers import TaggableManager @@ -2730,6 +2730,24 @@ class VirtualChassis(ChangeLoggedModel): 'master': "The selected master is not assigned to this virtual chassis." }) + def delete(self, *args, **kwargs): + + # Check for LAG interfaces split across member chassis + interfaces = Interface.objects.filter( + device__in=self.members.all(), + lag__isnull=False + ).exclude( + lag__device=F('device') + ) + if interfaces: + raise ProtectedError( + "Unable to delete virtual chassis {}. There are member interfaces which form a cross-chassis " + "LAG".format(self), + interfaces + ) + + return super().delete(*args, **kwargs) + def to_csv(self): return ( self.master, From 6a6959d0411eee32fe06be66e83ceec1f7262824 Mon Sep 17 00:00:00 2001 From: hSaria <34197532+hSaria@users.noreply.github.com> Date: Fri, 13 Dec 2019 18:06:14 +0000 Subject: [PATCH 06/59] Fixes #3761: copy button for tokens --- netbox/templates/users/api_tokens.html | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/netbox/templates/users/api_tokens.html b/netbox/templates/users/api_tokens.html index c6a219381..b775af73e 100644 --- a/netbox/templates/users/api_tokens.html +++ b/netbox/templates/users/api_tokens.html @@ -10,6 +10,7 @@
+ Copy {% if perms.users.change_token %} Edit {% endif %} @@ -17,7 +18,8 @@ Delete {% endif %}
- {{ token.key }} + + {{ token.key }} {% if token.is_expired %} Expired {% endif %} @@ -66,3 +68,9 @@
{% endblock %} + +{% block javascript %} + +{% endblock %} From ea51aa97b764e83ce6780453b443015e279b595c Mon Sep 17 00:00:00 2001 From: hSaria <34197532+hSaria@users.noreply.github.com> Date: Fri, 13 Dec 2019 18:08:34 +0000 Subject: [PATCH 07/59] Update version-2.6.md --- docs/release-notes/version-2.6.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 5a64ceb9e..7acf93cc7 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -4,6 +4,7 @@ * [#3152](https://github.com/netbox-community/netbox/issues/3152) - Include direct link to rack elevations on site view * [#3441](https://github.com/netbox-community/netbox/issues/3441) - Move virtual machine results near devices in global search +* [#3761](https://github.com/netbox-community/netbox/issues/3761) - Added copy button for API tokens ## Bug Fixes From a22c7c1539b67e9b19717927f1b25a71c31f6eb2 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 13 Dec 2019 14:15:48 -0500 Subject: [PATCH 08/59] Fixes #2358: Respect custom field default values when creating objects via the REST API --- docs/release-notes/version-2.6.md | 1 + netbox/extras/api/customfields.py | 29 ++++++++++++++++---- netbox/extras/tests/test_customfields.py | 34 ++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 5a64ceb9e..599c20c3b 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -8,6 +8,7 @@ ## Bug Fixes * [#2170](https://github.com/netbox-community/netbox/issues/2170) - Prevent the deletion of a virtual chassis when a cross-member LAG is present +* [#2358](https://github.com/netbox-community/netbox/issues/2358) - Respect custom field default values when creating objects via the REST API * [#3749](https://github.com/netbox-community/netbox/issues/3749) - Fix exception on password change page for local users # v2.6.8 (2019-12-10) diff --git a/netbox/extras/api/customfields.py b/netbox/extras/api/customfields.py index 42dc486b8..2a13e5ce1 100644 --- a/netbox/extras/api/customfields.py +++ b/netbox/extras/api/customfields.py @@ -22,7 +22,9 @@ class CustomFieldsSerializer(serializers.BaseSerializer): def to_internal_value(self, data): content_type = ContentType.objects.get_for_model(self.parent.Meta.model) - custom_fields = {field.name: field for field in CustomField.objects.filter(obj_type=content_type)} + custom_fields = { + field.name: field for field in CustomField.objects.filter(obj_type=content_type) + } for field_name, value in data.items(): @@ -107,11 +109,11 @@ class CustomFieldModelSerializer(ValidatedModelSerializer): super().__init__(*args, **kwargs) - if self.instance is not None: + # Retrieve the set of CustomFields which apply to this type of object + content_type = ContentType.objects.get_for_model(self.Meta.model) + fields = CustomField.objects.filter(obj_type=content_type) - # Retrieve the set of CustomFields which apply to this type of object - content_type = ContentType.objects.get_for_model(self.Meta.model) - fields = CustomField.objects.filter(obj_type=content_type) + if self.instance is not None: # Populate CustomFieldValues for each instance from database try: @@ -120,6 +122,23 @@ class CustomFieldModelSerializer(ValidatedModelSerializer): except TypeError: _populate_custom_fields(self.instance, fields) + else: + + # Populate default values + if fields and 'custom_fields' not in self.initial_data: + self.initial_data['custom_fields'] = {} + + # Populate initial data using custom field default values + for field in fields: + if field.name not in self.initial_data['custom_fields'] and field.default: + if field.type == CF_TYPE_SELECT: + field_value = field.choices.get(value=field.default).pk + elif field.type == CF_TYPE_BOOLEAN: + field_value = bool(field.default) + else: + field_value = field.default + self.initial_data['custom_fields'][field.name] = field_value + def _save_custom_fields(self, instance, custom_fields): content_type = ContentType.objects.get_for_model(self.Meta.model) for field_name, value in custom_fields.items(): diff --git a/netbox/extras/tests/test_customfields.py b/netbox/extras/tests/test_customfields.py index 96f3483bc..7db4e26d9 100644 --- a/netbox/extras/tests/test_customfields.py +++ b/netbox/extras/tests/test_customfields.py @@ -301,6 +301,40 @@ class CustomFieldAPITest(APITestCase): cfv = self.site.custom_field_values.get(field=self.cf_select) self.assertEqual(cfv.value.pk, data['custom_fields']['magic_choice']) + def test_set_custom_field_defaults(self): + """ + Create a new object with no custom field data. Custom field values should be created using the custom fields' + default values. + """ + CUSTOM_FIELD_DEFAULTS = { + 'magic_word': 'foobar', + 'magic_number': '123', + 'is_magic': 'true', + 'magic_date': '2019-12-13', + 'magic_url': 'http://example.com/', + 'magic_choice': self.cf_select_choice1.value, + } + + # Update CustomFields to set default values + for field_name, default_value in CUSTOM_FIELD_DEFAULTS.items(): + CustomField.objects.filter(name=field_name).update(default=default_value) + + data = { + 'name': 'Test Site X', + 'slug': 'test-site-x', + } + + url = reverse('dcim-api:site-list') + response = self.client.post(url, data, format='json', **self.header) + + self.assertHttpStatus(response, status.HTTP_201_CREATED) + self.assertEqual(response.data['custom_fields']['magic_word'], CUSTOM_FIELD_DEFAULTS['magic_word']) + self.assertEqual(response.data['custom_fields']['magic_number'], str(CUSTOM_FIELD_DEFAULTS['magic_number'])) + self.assertEqual(response.data['custom_fields']['is_magic'], bool(CUSTOM_FIELD_DEFAULTS['is_magic'])) + self.assertEqual(response.data['custom_fields']['magic_date'], CUSTOM_FIELD_DEFAULTS['magic_date']) + self.assertEqual(response.data['custom_fields']['magic_url'], CUSTOM_FIELD_DEFAULTS['magic_url']) + self.assertEqual(response.data['custom_fields']['magic_choice'], self.cf_select_choice1.pk) + class CustomFieldChoiceAPITest(APITestCase): def setUp(self): From 1d1cb867cd0fbded359421143213f2edfa020c60 Mon Sep 17 00:00:00 2001 From: kobayashi Date: Fri, 13 Dec 2019 14:24:11 -0500 Subject: [PATCH 09/59] fix 3679 --- docs/release-notes/version-2.6.md | 1 + netbox/ipam/tables.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 5a64ceb9e..538474912 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -9,6 +9,7 @@ * [#2170](https://github.com/netbox-community/netbox/issues/2170) - Prevent the deletion of a virtual chassis when a cross-member LAG is present * [#3749](https://github.com/netbox-community/netbox/issues/3749) - Fix exception on password change page for local users +* [#3757](https://github.com/netbox-community/netbox/issues/3757) - Fix unable to assign IP to interface # v2.6.8 (2019-12-10) diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index 91f195ba0..e4d2bf8b4 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -85,7 +85,11 @@ IPADDRESS_LINK = """ """ IPADDRESS_ASSIGN_LINK = """ -{{ record }} +{% if request.GET %} + {{ record }} +{% else %} + {{ record }} +{% endif %} """ IPADDRESS_PARENT = """ From e53e1e31de5e8836fad67048ed0944addda0550c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 16 Dec 2019 16:30:20 -0500 Subject: [PATCH 10/59] Release v2.6.9 --- docs/release-notes/version-2.6.md | 2 +- netbox/netbox/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 4d047cb69..afad93fde 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -1,4 +1,4 @@ -# v2.6.9 (FUTURE) +# v2.6.9 (2019-12-16) ## Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 413850725..701af3c12 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured # Environment setup # -VERSION = '2.6.9-dev' +VERSION = '2.6.9' # Hostname HOSTNAME = platform.node() From aff4ad0f9774856eff8a6fad906366899cb88209 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 16 Dec 2019 16:33:31 -0500 Subject: [PATCH 11/59] Post-release version bump --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 701af3c12..b60a6691c 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured # Environment setup # -VERSION = '2.6.9' +VERSION = '2.6.10-dev' # Hostname HOSTNAME = platform.node() From 55b503da5bdf2a95e903fb657ade195ff89cae91 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 19 Dec 2019 14:04:18 -0500 Subject: [PATCH 12/59] Fixes #3780: Fix AttributeError exception in API docs --- docs/release-notes/version-2.6.md | 12 ++++++++++++ netbox/extras/api/customfields.py | 3 +++ netbox/utilities/tests/test_api.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index afad93fde..7d131ffc9 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -1,3 +1,11 @@ +# v2.6.10 (FUTURE) + +## Bug Fixes + +* [#3780](https://github.com/netbox-community/netbox/issues/3780) - Fix AttributeError exception in API docs + +--- + # v2.6.9 (2019-12-16) ## Enhancements @@ -13,6 +21,8 @@ * [#3749](https://github.com/netbox-community/netbox/issues/3749) - Fix exception on password change page for local users * [#3757](https://github.com/netbox-community/netbox/issues/3757) - Fix unable to assign IP to interface +--- + # v2.6.8 (2019-12-10) ## Enhancements @@ -35,6 +45,8 @@ * [#3724](https://github.com/netbox-community/netbox/issues/3724) - Fix API filtering of interfaces by more than one device name * [#3725](https://github.com/netbox-community/netbox/issues/3725) - Enforce client validation for minimum service port number +--- + # v2.6.7 (2019-11-01) ## Enhancements diff --git a/netbox/extras/api/customfields.py b/netbox/extras/api/customfields.py index 2a13e5ce1..e0c70efa3 100644 --- a/netbox/extras/api/customfields.py +++ b/netbox/extras/api/customfields.py @@ -124,6 +124,9 @@ class CustomFieldModelSerializer(ValidatedModelSerializer): else: + if not hasattr(self, 'initial_data'): + self.initial_data = {} + # Populate default values if fields and 'custom_fields' not in self.initial_data: self.initial_data['custom_fields'] = {} diff --git a/netbox/utilities/tests/test_api.py b/netbox/utilities/tests/test_api.py index 3ff4b3876..4aea682f4 100644 --- a/netbox/utilities/tests/test_api.py +++ b/netbox/utilities/tests/test_api.py @@ -1,7 +1,13 @@ +import urllib.parse + +from django.contrib.contenttypes.models import ContentType +from django.test import Client, TestCase from django.urls import reverse from rest_framework import status from dcim.models import Region, Site +from extras.constants import CF_TYPE_TEXT +from extras.models import CustomField from ipam.models import VLAN from utilities.testing import APITestCase @@ -117,3 +123,26 @@ class WritableNestedSerializerTest(APITestCase): self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) self.assertEqual(VLAN.objects.count(), 0) + + +class APIDocsTestCase(TestCase): + + def setUp(self): + self.client = Client() + + # Populate a CustomField to activate CustomFieldSerializer + content_type = ContentType.objects.get_for_model(Site) + self.cf_text = CustomField(type=CF_TYPE_TEXT, name='test') + self.cf_text.save() + self.cf_text.obj_type.set([content_type]) + self.cf_text.save() + + def test_api_docs(self): + + url = reverse('api_docs') + params = { + "format": "openapi", + } + + response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params))) + self.assertEqual(response.status_code, 200) From 0174c9747b563f95bf0708a0f9a49aee2b13cc5d Mon Sep 17 00:00:00 2001 From: Sander Steffann Date: Tue, 17 Dec 2019 16:54:26 +0100 Subject: [PATCH 13/59] Implement request passing as a property of Script --- netbox/extras/scripts.py | 9 ++++++++- netbox/extras/views.py | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 4e0934a6a..28238b008 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -235,6 +235,9 @@ class BaseScript: # Initiate the log self.log = [] + # Declare the placeholder for the current request + self.request = None + # Grab some info about the script self.filename = inspect.getfile(self.__class__) self.source = inspect.getsource(self.__class__) @@ -337,7 +340,7 @@ def is_variable(obj): return isinstance(obj, ScriptVariable) -def run_script(script, data, files, commit=True): +def run_script(script, data, request, commit=True): """ A wrapper for calling Script.run(). This performs error handling and provides a hook for committing changes. It exists outside of the Script class to ensure it cannot be overridden by a script author. @@ -347,9 +350,13 @@ def run_script(script, data, files, commit=True): end_time = None # Add files to form data + files = request.FILES for field_name, fileobj in files.items(): data[field_name] = fileobj + # Add the current request as a property of the script + script.request = request + try: with transaction.atomic(): start_time = time.time() diff --git a/netbox/extras/views.py b/netbox/extras/views.py index c8dc2f374..eb17a65ab 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -413,7 +413,7 @@ class ScriptView(PermissionRequiredMixin, View): if form.is_valid(): commit = form.cleaned_data.pop('_commit') - output, execution_time = run_script(script, form.cleaned_data, request.FILES, commit) + output, execution_time = run_script(script, form.cleaned_data, request, commit) return render(request, 'extras/script.html', { 'module': module, From d31507985b4549cad8cbef983583b33e9f4e9654 Mon Sep 17 00:00:00 2001 From: struppi Date: Wed, 25 Dec 2019 18:41:59 +0100 Subject: [PATCH 14/59] Closes #3663: add Filter Tests --- netbox/extras/tests/test_api.py | 78 ++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 3fe36c9ef..be45c94eb 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -1,8 +1,11 @@ +import time +import datetime + from django.contrib.contenttypes.models import ContentType from django.urls import reverse from rest_framework import status -from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Region, Site +from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Region, Site, RackGroup, RackRole, Rack from extras.constants import GRAPH_TYPE_SITE from extras.models import ConfigContext, Graph, ExportTemplate, Tag from tenancy.models import Tenant, TenantGroup @@ -520,3 +523,76 @@ class ConfigContextTest(APITestCase): configcontext6.sites.add(site2) rendered_context = device.get_config_context() self.assertEqual(rendered_context['bar'], 456) + + +class CreatedUpdatedFilterTest(APITestCase): + + def setUp(self): + + super().setUp() + + self.site1 = Site.objects.create(name='Test Site 1', slug='test-site-1') + self.rackgroup1 = RackGroup.objects.create(site=self.site1, name='Test Rack Group 1', slug='test-rack-group-1') + self.rackrole1 = RackRole.objects.create(name='Test Rack Role 1', slug='test-rack-role-1', color='ff0000') + self.rack1 = Rack.objects.create( + site=self.site1, group=self.rackgroup1, role=self.rackrole1, name='Test Rack 1', u_height=42, + ) + self.rack2 = Rack.objects.create( + site=self.site1, group=self.rackgroup1, role=self.rackrole1, name='Test Rack 2', u_height=42, + ) + + # change the created and last_updated of one + time.sleep(2) # to have a difference on the last_updated + self.rack2.created = datetime.datetime(2001, 2, 3) + self.rack2.save() + + def test_get_rack_meta(self): + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.rack1.pk}) + response = self.client.get(url, **self.header) + + self.assertEqual(response.data['created'], self.rack1.created.strftime("%Y-%m-%d")) + self.assertEqual(response.data['last_updated'], self.rack1.last_updated.strftime("%Y-%m-%dT%H:%M:%S.%fZ")) + + def test_get_rack_created(self): + url = reverse('dcim-api:rack-list') + response = self.client.get('{}?created=2001-02-03'.format(url), **self.header) + + self.assertEqual(response.data['count'], 1) + + def test_get_rack_created_gte(self): + url = reverse('dcim-api:rack-list') + response = self.client.get('{}?created__gte=2001-02-04'.format(url), **self.header) + + self.assertEqual(response.data['count'], 1) + + def test_get_rack_created_lte(self): + url = reverse('dcim-api:rack-list') + response = self.client.get('{}?created__lte=2001-02-04'.format(url), **self.header) + + self.assertEqual(response.data['count'], 1) + + def test_get_rack_last_updated(self): + last_updated = self.rack2.last_updated.strftime("%Y-%m-%d %H:%M:%S.%f") + + url = reverse('dcim-api:rack-list') + response = self.client.get(('{}?last_updated='+last_updated).format(url), **self.header) + + self.assertEqual(response.data['count'], 1) + + def test_get_rack_last_updated_gte(self): + last_updated_delta = self.rack1.last_updated + datetime.timedelta(0, 1) + last_updated = last_updated_delta.strftime("%Y-%m-%d %H:%M:%S") + + url = reverse('dcim-api:rack-list') + response = self.client.get(('{}?last_updated__gte='+last_updated).format(url), **self.header) + + self.assertEqual(response.data['count'], 1) + + def test_get_rack_last_updated_lte(self): + last_updated_delta = self.rack1.last_updated + datetime.timedelta(0, 1) + last_updated = last_updated_delta.strftime("%Y-%m-%d %H:%M:%S") + + url = reverse('dcim-api:rack-list') + response = self.client.get(('{}?last_updated__lte=' + last_updated).format(url), **self.header) + + self.assertEqual(response.data['count'], 1) From 407a60dcc47528e2d992de613b182b4b9faeebf7 Mon Sep 17 00:00:00 2001 From: struppi Date: Thu, 26 Dec 2019 12:26:41 +0100 Subject: [PATCH 15/59] Closes #3663: fix PEP errors --- netbox/extras/tests/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index be45c94eb..3ed7eb13e 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -575,7 +575,7 @@ class CreatedUpdatedFilterTest(APITestCase): last_updated = self.rack2.last_updated.strftime("%Y-%m-%d %H:%M:%S.%f") url = reverse('dcim-api:rack-list') - response = self.client.get(('{}?last_updated='+last_updated).format(url), **self.header) + response = self.client.get(('{}?last_updated=' + last_updated).format(url), **self.header) self.assertEqual(response.data['count'], 1) @@ -584,7 +584,7 @@ class CreatedUpdatedFilterTest(APITestCase): last_updated = last_updated_delta.strftime("%Y-%m-%d %H:%M:%S") url = reverse('dcim-api:rack-list') - response = self.client.get(('{}?last_updated__gte='+last_updated).format(url), **self.header) + response = self.client.get(('{}?last_updated__gte=' + last_updated).format(url), **self.header) self.assertEqual(response.data['count'], 1) From adeee0bf5c3ffedf24faab5e5a098884e2c74572 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 26 Dec 2019 10:16:53 -0500 Subject: [PATCH 16/59] Docs & changelog for #3705 --- docs/additional-features/custom-scripts.md | 12 ++++++++++++ docs/release-notes/version-2.6.md | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/docs/additional-features/custom-scripts.md b/docs/additional-features/custom-scripts.md index cdb49c82a..c4dffb4b9 100644 --- a/docs/additional-features/custom-scripts.md +++ b/docs/additional-features/custom-scripts.md @@ -71,6 +71,18 @@ The checkbox to commit database changes when executing a script is checked by de commit_default = False ``` +## Accessing Request Data + +Details of the current HTTP request (the one being made to execute the script) are available as the instance attribute `self.request`. This can be used to infer, for example, the user executing the script and the client IP address: + +```python +username = self.request.user.username +ip_address = self.request.META.get('HTTP_X_FORWARDED_FOR') or self.request.META.get('REMOTE_ADDR') +self.log_info("Running as user {} (IP: {})...".format(username, ip_address)) +``` + +For a complete list of available request parameters, please see the [Django documentation](https://docs.djangoproject.com/en/stable/ref/request-response/). + ## Reading Data from Files The Script class provides two convenience methods for reading data from files: diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 7d131ffc9..67da3b7e2 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -1,5 +1,9 @@ # v2.6.10 (FUTURE) +## Enhancements + +* [#3705](https://github.com/netbox-community/netbox/issues/3705) - Provide request context when executing custom scripts + ## Bug Fixes * [#3780](https://github.com/netbox-community/netbox/issues/3780) - Fix AttributeError exception in API docs From 5fedcd1f4e1d7d3a775af06f66a06a1addb5b709 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 26 Dec 2019 10:19:32 -0500 Subject: [PATCH 17/59] Fixes #3789: Typo --- docs/installation/2-netbox.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/2-netbox.md b/docs/installation/2-netbox.md index 7bae23d77..6d2706eb0 100644 --- a/docs/installation/2-netbox.md +++ b/docs/installation/2-netbox.md @@ -14,7 +14,7 @@ This section of the documentation discusses installing and configuring the NetBo # yum install -y epel-release # yum install -y gcc python36 python36-devel python36-setuptools libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel redhat-rpm-config redis # easy_install-3.6 pip -# ln -s /usr/bin/python36 /usr/bin/python3 +# ln -s /usr/bin/python3.6 /usr/bin/python3 ``` You may opt to install NetBox either from a numbered release or by cloning the master branch of its repository on GitHub. From fbb93c72d0bd3ef49648fa1bc63585763ed46b36 Mon Sep 17 00:00:00 2001 From: struppi Date: Thu, 26 Dec 2019 22:21:05 +0100 Subject: [PATCH 18/59] Closes #3663: improve tests --- netbox/extras/tests/test_api.py | 38 +++++++++++++-------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 3ed7eb13e..d419f2d7c 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -1,11 +1,11 @@ -import time import datetime from django.contrib.contenttypes.models import ContentType from django.urls import reverse +from django.utils import timezone from rest_framework import status -from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Region, Site, RackGroup, RackRole, Rack +from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Rack, RackGroup, RackRole, Region, Site from extras.constants import GRAPH_TYPE_SITE from extras.models import ConfigContext, Graph, ExportTemplate, Tag from tenancy.models import Tenant, TenantGroup @@ -542,57 +542,49 @@ class CreatedUpdatedFilterTest(APITestCase): ) # change the created and last_updated of one - time.sleep(2) # to have a difference on the last_updated - self.rack2.created = datetime.datetime(2001, 2, 3) - self.rack2.save() - - def test_get_rack_meta(self): - url = reverse('dcim-api:rack-detail', kwargs={'pk': self.rack1.pk}) - response = self.client.get(url, **self.header) - - self.assertEqual(response.data['created'], self.rack1.created.strftime("%Y-%m-%d")) - self.assertEqual(response.data['last_updated'], self.rack1.last_updated.strftime("%Y-%m-%dT%H:%M:%S.%fZ")) + Rack.objects.filter(pk=self.rack2.pk).update( + last_updated=datetime.datetime(2001, 2, 3, 1, 2, 3, 4, tzinfo=timezone.utc), + created=datetime.datetime(2001, 2, 3) + ) def test_get_rack_created(self): url = reverse('dcim-api:rack-list') response = self.client.get('{}?created=2001-02-03'.format(url), **self.header) self.assertEqual(response.data['count'], 1) + self.assertEqual(response.data['results'][0]['id'], self.rack2.pk) def test_get_rack_created_gte(self): url = reverse('dcim-api:rack-list') response = self.client.get('{}?created__gte=2001-02-04'.format(url), **self.header) self.assertEqual(response.data['count'], 1) + self.assertEqual(response.data['results'][0]['id'], self.rack1.pk) def test_get_rack_created_lte(self): url = reverse('dcim-api:rack-list') response = self.client.get('{}?created__lte=2001-02-04'.format(url), **self.header) self.assertEqual(response.data['count'], 1) + self.assertEqual(response.data['results'][0]['id'], self.rack2.pk) def test_get_rack_last_updated(self): - last_updated = self.rack2.last_updated.strftime("%Y-%m-%d %H:%M:%S.%f") - url = reverse('dcim-api:rack-list') - response = self.client.get(('{}?last_updated=' + last_updated).format(url), **self.header) + response = self.client.get('{}?last_updated=2001-02-03%2001:02:03.000004'.format(url), **self.header) self.assertEqual(response.data['count'], 1) + self.assertEqual(response.data['results'][0]['id'], self.rack2.pk) def test_get_rack_last_updated_gte(self): - last_updated_delta = self.rack1.last_updated + datetime.timedelta(0, 1) - last_updated = last_updated_delta.strftime("%Y-%m-%d %H:%M:%S") - url = reverse('dcim-api:rack-list') - response = self.client.get(('{}?last_updated__gte=' + last_updated).format(url), **self.header) + response = self.client.get('{}?last_updated__gte=2001-02-04%2001:02:03.000004'.format(url), **self.header) self.assertEqual(response.data['count'], 1) + self.assertEqual(response.data['results'][0]['id'], self.rack1.pk) def test_get_rack_last_updated_lte(self): - last_updated_delta = self.rack1.last_updated + datetime.timedelta(0, 1) - last_updated = last_updated_delta.strftime("%Y-%m-%d %H:%M:%S") - url = reverse('dcim-api:rack-list') - response = self.client.get(('{}?last_updated__lte=' + last_updated).format(url), **self.header) + response = self.client.get('{}?last_updated__lte=2001-02-04%2001:02:03.000004'.format(url), **self.header) self.assertEqual(response.data['count'], 1) + self.assertEqual(response.data['results'][0]['id'], self.rack2.pk) From 14401c30b62ec869cefb4e3b932eaef1c62f6351 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 27 Dec 2019 13:48:41 -0500 Subject: [PATCH 19/59] Update contributing guide to reference the issue intake policy --- CONTRIBUTING.md | 59 +++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55a979eef..cceea27b6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ For real-time discussion, you can join the #netbox Slack channel on [NetworkToCo ## Reporting Bugs -* First, ensure that you've installed the [latest stable version](https://github.com/netbox-community/netbox/releases) +* First, ensure that you're running the [latest stable version](https://github.com/netbox-community/netbox/releases) of NetBox. If you're running an older version, it's possible that the bug has already been fixed. @@ -28,27 +28,26 @@ up (+1). You might also want to add a comment describing how it's affecting your installation. This will allow us to prioritize bugs based on how many users are affected. -* If you haven't found an existing issue that describes your suspected bug, -please inquire about it on the mailing list. **Do not** file an issue until you -have received confirmation that it is in fact a bug. Invalid issues are very -distracting and slow the pace at which NetBox is developed. - * When submitting an issue, please be as descriptive as possible. Be sure to -include: +provide all information request in the issue template, including: * The environment in which NetBox is running - * The exact steps that can be taken to reproduce the issue (if applicable) + * The exact steps that can be taken to reproduce the issue + * Expected and observed behavior * Any error messages generated * Screenshots (if applicable) * Please avoid prepending any sort of tag (e.g. "[Bug]") to the issue title. -The issue will be reviewed by a moderator after submission and the appropriate +The issue will be reviewed by a maintainer after submission and the appropriate labels will be applied for categorization. * Keep in mind that we prioritize bugs based on their severity and how much work is required to resolve them. It may take some time for someone to address your issue. +* For more information on how bug reports are handled, please see our [issue +intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Policy). + ## Feature Requests * First, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues) @@ -61,10 +60,10 @@ free to add a comment with any additional justification for the feature. (However, note that comments with no substance other than a "+1" will be deleted. Please use GitHub's reactions feature to indicate your support.) -* Due to an excessive backlog of feature requests, we are not currently -accepting any proposals which substantially extend NetBox's functionality -beyond its current feature set. This includes the introduction of any new views -or models which have not already been proposed in an existing feature request. +* Due to a large backlog of feature requests, we are not currently accepting +any proposals which substantially extend NetBox's functionality beyond its +current feature set. This includes the introduction of any new views or models +which have not already been proposed in an existing feature request. * Before filing a new feature request, consider raising your idea on the mailing list first. Feedback you receive there will help validate and shape the @@ -75,8 +74,8 @@ describe the functionality and data model(s) being proposed. The more effort you put into writing a feature request, the better its chance is of being implemented. Overly broad feature requests will be closed. -* When submitting a feature request on GitHub, be sure to include the -following: +* When submitting a feature request on GitHub, be sure to include all +information requested by the issue template, including: * A detailed description of the proposed functionality * A use case for the feature; who would use it and what value it would add @@ -89,6 +88,9 @@ following: title. The issue will be reviewed by a moderator after submission and the appropriate labels will be applied for categorization. +* For more information on how feature requests are handled, please see our +[issue intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Policy). + ## Submitting Pull Requests * Be sure to open an issue **before** starting work on a pull request, and @@ -103,7 +105,7 @@ any work that's already in progress. * When submitting a pull request, please be sure to work off of the `develop` branch, rather than `master`. The `develop` branch is used for ongoing -development, while `master` is used for tagging new stable releases. +development, while `master` is used for tagging stable releases. * All code submissions should meet the following criteria (CI will enforce these checks): @@ -122,27 +124,26 @@ reduce noise in the discussion. ## Issue Lifecycle -When a correctly formatted issue is submitted it is evaluated by a moderator -who may elect to immediately label the issue as accepted in addition to another -issue type label. In other cases, the issue may be labeled as "status: gathering feedback" -which will often be accompanied by a comment from a moderator asking for further dialog from the community. -If an issue is labeled as "status: revisions needed" a moderator has identified a problem with -the issue itself and is asking for the submitter himself to update the original post with -the requested information. If the original post is not updated in a reasonable amount of time, -the issue will be closed as invalid. +New issues are handled according to our [issue intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Policy). +Maintainers will assign label(s) and/or close new issues as the policy +dictates. This helps ensure a productive development environment and avoid +accumulating a large backlog of work. -The core maintainers group has chosen to make use of the GitHub Stale bot to aid in issue management. +The core maintainers group has chosen to make use of GitHub's [Stale bot](https://github.com/apps/stale) +to aid in issue management. * Issues will be marked as stale after 14 days of no activity. * Then after 7 more days of inactivity, the issue will be closed. -* Any issue bearing one of the following labels will be exempt from all Stale bot actions: +* Any issue bearing one of the following labels will be exempt from all Stale + bot actions: * `status: accepted` * `status: gathering feedback` * `status: blocked` -It is natural that some new issues get more attention than others. Often this is a metric of an issues's -overall usefulness to the project. In other cases in which issues merely get lost in the shuffle, -notifications from Stale bot can bring renewed attention to potentially meaningful issues. +It is natural that some new issues get more attention than others. Often this +is a metric of an issues's overall value to the project. In other cases in +which issues merely get lost in the shuffle, notifications from Stale bot can +bring renewed attention to potentially meaningful issues. ## Maintainer Guidance From ae9d0d894a20ecb2f8650fc1a0401b2aa4815035 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 27 Dec 2019 14:04:03 -0500 Subject: [PATCH 20/59] Fixes #3695: Include A/Z termination sites for circuits in global search --- docs/release-notes/version-2.6.md | 1 + netbox/netbox/views.py | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 67da3b7e2..5d5eb00df 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -6,6 +6,7 @@ ## Bug Fixes +* [#3695](https://github.com/netbox-community/netbox/issues/3695) - Include A/Z termination sites for circuits in global search * [#3780](https://github.com/netbox-community/netbox/issues/3780) - Fix AttributeError exception in API docs --- diff --git a/netbox/netbox/views.py b/netbox/netbox/views.py index 5dee6cade..da5fec24e 100644 --- a/netbox/netbox/views.py +++ b/netbox/netbox/views.py @@ -1,6 +1,6 @@ from collections import OrderedDict -from django.db.models import Count, F +from django.db.models import Count, F, OuterRef, Subquery from django.shortcuts import render from django.views.generic import View from rest_framework.response import Response @@ -8,7 +8,7 @@ from rest_framework.reverse import reverse from rest_framework.views import APIView from circuits.filters import CircuitFilter, ProviderFilter -from circuits.models import Circuit, Provider +from circuits.models import Circuit, CircuitTermination, Provider from circuits.tables import CircuitTable, ProviderTable from dcim.filters import ( CableFilter, DeviceFilter, DeviceTypeFilter, PowerFeedFilter, RackFilter, RackGroupFilter, SiteFilter, @@ -49,9 +49,15 @@ SEARCH_TYPES = OrderedDict(( ('circuit', { 'permission': 'circuits.view_circuit', 'queryset': Circuit.objects.prefetch_related( - 'type', 'provider', 'tenant' - ).prefetch_related( - 'terminations__site' + 'type', 'provider', 'tenant', 'terminations__site' + ).annotate( + # Annotate A/Z terminations + a_side=Subquery( + CircuitTermination.objects.filter(circuit=OuterRef('pk')).filter(term_side='A').values('site__name')[:1] + ), + z_side=Subquery( + CircuitTermination.objects.filter(circuit=OuterRef('pk')).filter(term_side='Z').values('site__name')[:1] + ), ), 'filter': CircuitFilter, 'table': CircuitTable, From e6623a6ca8cf305830e8c217994f80fc45ec3245 Mon Sep 17 00:00:00 2001 From: kobayashi Date: Fri, 27 Dec 2019 16:17:17 -0500 Subject: [PATCH 21/59] implement 3788 --- docs/release-notes/version-2.6.md | 1 + netbox/dcim/filters.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 5d5eb00df..595dd1c14 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -3,6 +3,7 @@ ## Enhancements * [#3705](https://github.com/netbox-community/netbox/issues/3705) - Provide request context when executing custom scripts +* [#3788](https://github.com/netbox-community/netbox/issues/3788) - Enabled partial search for inventory items ## Bug Fixes diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index cea279ddd..111469d98 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -868,8 +868,8 @@ class InventoryItemFilter(DeviceComponentFilterSet): qs_filter = ( Q(name__icontains=value) | Q(part_id__icontains=value) | - Q(serial__iexact=value) | - Q(asset_tag__iexact=value) | + Q(serial__icontains=value) | + Q(asset_tag__icontains=value) | Q(description__icontains=value) ) return queryset.filter(qs_filter) From 6dea8ddbce907a0de8c81c20fc4002cc858a36f0 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Sat, 28 Dec 2019 21:31:21 +0000 Subject: [PATCH 22/59] Flatpickr library statics --- .../flatpickr-4.6.3/flatpickr.min.js | 2 + .../flatpickr-4.6.3/themes/dark.css | 784 +++++++++++++++++ .../flatpickr-4.6.3/themes/light.css | 798 ++++++++++++++++++ 3 files changed, 1584 insertions(+) create mode 100644 netbox/project-static/flatpickr-4.6.3/flatpickr.min.js create mode 100644 netbox/project-static/flatpickr-4.6.3/themes/dark.css create mode 100644 netbox/project-static/flatpickr-4.6.3/themes/light.css diff --git a/netbox/project-static/flatpickr-4.6.3/flatpickr.min.js b/netbox/project-static/flatpickr-4.6.3/flatpickr.min.js new file mode 100644 index 000000000..c850b7cfd --- /dev/null +++ b/netbox/project-static/flatpickr-4.6.3/flatpickr.min.js @@ -0,0 +1,2 @@ +/* flatpickr v4.6.3,, @license MIT */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).flatpickr=t()}(this,function(){"use strict";var e=function(){return(e=Object.assign||function(e){for(var t,n=1,a=arguments.length;n",noCalendar:!1,now:new Date,onChange:[],onClose:[],onDayCreate:[],onDestroy:[],onKeyDown:[],onMonthChange:[],onOpen:[],onParseConfig:[],onReady:[],onValueUpdate:[],onYearChange:[],onPreCalendarPosition:[],plugins:[],position:"auto",positionElement:void 0,prevArrow:"",shorthandCurrentMonth:!1,showMonths:1,static:!1,time_24hr:!1,weekNumbers:!1,wrap:!1},a={weekdays:{shorthand:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],longhand:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},months:{shorthand:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],longhand:["January","February","March","April","May","June","July","August","September","October","November","December"]},daysInMonth:[31,28,31,30,31,30,31,31,30,31,30,31],firstDayOfWeek:0,ordinal:function(e){var t=e%100;if(t>3&&t<21)return"th";switch(t%10){case 1:return"st";case 2:return"nd";case 3:return"rd";default:return"th"}},rangeSeparator:" to ",weekAbbreviation:"Wk",scrollTitle:"Scroll to increment",toggleTitle:"Click to toggle",amPM:["AM","PM"],yearAriaLabel:"Year",hourAriaLabel:"Hour",minuteAriaLabel:"Minute",time_24hr:!1},i=function(e){return("0"+e).slice(-2)},o=function(e){return!0===e?1:0};function r(e,t,n){var a;return void 0===n&&(n=!1),function(){var i=this,o=arguments;null!==a&&clearTimeout(a),a=window.setTimeout(function(){a=null,n||e.apply(i,o)},t),n&&!a&&e.apply(i,o)}}var l=function(e){return e instanceof Array?e:[e]};function c(e,t,n){if(!0===n)return e.classList.add(t);e.classList.remove(t)}function d(e,t,n){var a=window.document.createElement(e);return t=t||"",n=n||"",a.className=t,void 0!==n&&(a.textContent=n),a}function s(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function u(e,t){var n=d("div","numInputWrapper"),a=d("input","numInput "+e),i=d("span","arrowUp"),o=d("span","arrowDown");if(-1===navigator.userAgent.indexOf("MSIE 9.0")?a.type="number":(a.type="text",a.pattern="\\d*"),void 0!==t)for(var r in t)a.setAttribute(r,t[r]);return n.appendChild(a),n.appendChild(i),n.appendChild(o),n}var f=function(){},m=function(e,t,n){return n.months[t?"shorthand":"longhand"][e]},g={D:f,F:function(e,t,n){e.setMonth(n.months.longhand.indexOf(t))},G:function(e,t){e.setHours(parseFloat(t))},H:function(e,t){e.setHours(parseFloat(t))},J:function(e,t){e.setDate(parseFloat(t))},K:function(e,t,n){e.setHours(e.getHours()%12+12*o(new RegExp(n.amPM[1],"i").test(t)))},M:function(e,t,n){e.setMonth(n.months.shorthand.indexOf(t))},S:function(e,t){e.setSeconds(parseFloat(t))},U:function(e,t){return new Date(1e3*parseFloat(t))},W:function(e,t,n){var a=parseInt(t),i=new Date(e.getFullYear(),0,2+7*(a-1),0,0,0,0);return i.setDate(i.getDate()-i.getDay()+n.firstDayOfWeek),i},Y:function(e,t){e.setFullYear(parseFloat(t))},Z:function(e,t){return new Date(t)},d:function(e,t){e.setDate(parseFloat(t))},h:function(e,t){e.setHours(parseFloat(t))},i:function(e,t){e.setMinutes(parseFloat(t))},j:function(e,t){e.setDate(parseFloat(t))},l:f,m:function(e,t){e.setMonth(parseFloat(t)-1)},n:function(e,t){e.setMonth(parseFloat(t)-1)},s:function(e,t){e.setSeconds(parseFloat(t))},u:function(e,t){return new Date(parseFloat(t))},w:f,y:function(e,t){e.setFullYear(2e3+parseFloat(t))}},p={D:"(\\w+)",F:"(\\w+)",G:"(\\d\\d|\\d)",H:"(\\d\\d|\\d)",J:"(\\d\\d|\\d)\\w+",K:"",M:"(\\w+)",S:"(\\d\\d|\\d)",U:"(.+)",W:"(\\d\\d|\\d)",Y:"(\\d{4})",Z:"(.+)",d:"(\\d\\d|\\d)",h:"(\\d\\d|\\d)",i:"(\\d\\d|\\d)",j:"(\\d\\d|\\d)",l:"(\\w+)",m:"(\\d\\d|\\d)",n:"(\\d\\d|\\d)",s:"(\\d\\d|\\d)",u:"(.+)",w:"(\\d\\d|\\d)",y:"(\\d{2})"},h={Z:function(e){return e.toISOString()},D:function(e,t,n){return t.weekdays.shorthand[h.w(e,t,n)]},F:function(e,t,n){return m(h.n(e,t,n)-1,!1,t)},G:function(e,t,n){return i(h.h(e,t,n))},H:function(e){return i(e.getHours())},J:function(e,t){return void 0!==t.ordinal?e.getDate()+t.ordinal(e.getDate()):e.getDate()},K:function(e,t){return t.amPM[o(e.getHours()>11)]},M:function(e,t){return m(e.getMonth(),!0,t)},S:function(e){return i(e.getSeconds())},U:function(e){return e.getTime()/1e3},W:function(e,t,n){return n.getWeek(e)},Y:function(e){return e.getFullYear()},d:function(e){return i(e.getDate())},h:function(e){return e.getHours()%12?e.getHours()%12:12},i:function(e){return i(e.getMinutes())},j:function(e){return e.getDate()},l:function(e,t){return t.weekdays.longhand[e.getDay()]},m:function(e){return i(e.getMonth()+1)},n:function(e){return e.getMonth()+1},s:function(e){return e.getSeconds()},u:function(e){return e.getTime()},w:function(e){return e.getDay()},y:function(e){return String(e.getFullYear()).substring(2)}},v=function(e){var t=e.config,i=void 0===t?n:t,o=e.l10n,r=void 0===o?a:o;return function(e,t,n){var a=n||r;return void 0!==i.formatDate?i.formatDate(e,t,a):t.split("").map(function(t,n,o){return h[t]&&"\\"!==o[n-1]?h[t](e,a,i):"\\"!==t?t:""}).join("")}},D=function(e){var t=e.config,i=void 0===t?n:t,o=e.l10n,r=void 0===o?a:o;return function(e,t,a,o){if(0===e||e){var l,c=o||r,d=e;if(e instanceof Date)l=new Date(e.getTime());else if("string"!=typeof e&&void 0!==e.toFixed)l=new Date(e);else if("string"==typeof e){var s=t||(i||n).dateFormat,u=String(e).trim();if("today"===u)l=new Date,a=!0;else if(/Z$/.test(u)||/GMT$/.test(u))l=new Date(e);else if(i&&i.parseDate)l=i.parseDate(e,s);else{l=i&&i.noCalendar?new Date((new Date).setHours(0,0,0,0)):new Date((new Date).getFullYear(),0,1,0,0,0,0);for(var f=void 0,m=[],h=0,v=0,D="";hMath.min(t,n)&&er&&(s=n===h.hourElement?s-r-o(!h.amPM):a,f&&j(void 0,1,h.hourElement)),h.amPM&&u&&(1===l?s+c===23:Math.abs(s-c)>l)&&(h.amPM.textContent=h.l10n.amPM[o(h.amPM.textContent===h.l10n.amPM[0])]),n.value=i(s)}}(e);var t=h._input.value;k(),we(),h._input.value!==t&&h._debouncedChange()}function k(){if(void 0!==h.hourElement&&void 0!==h.minuteElement){var e,t,n=(parseInt(h.hourElement.value.slice(-2),10)||0)%24,a=(parseInt(h.minuteElement.value,10)||0)%60,i=void 0!==h.secondElement?(parseInt(h.secondElement.value,10)||0)%60:0;void 0!==h.amPM&&(e=n,t=h.amPM.textContent,n=e%12+12*o(t===h.l10n.amPM[1]));var r=void 0!==h.config.minTime||h.config.minDate&&h.minDateHasTime&&h.latestSelectedDateObj&&0===w(h.latestSelectedDateObj,h.config.minDate,!0);if(void 0!==h.config.maxTime||h.config.maxDate&&h.maxDateHasTime&&h.latestSelectedDateObj&&0===w(h.latestSelectedDateObj,h.config.maxDate,!0)){var l=void 0!==h.config.maxTime?h.config.maxTime:h.config.maxDate;(n=Math.min(n,l.getHours()))===l.getHours()&&(a=Math.min(a,l.getMinutes())),a===l.getMinutes()&&(i=Math.min(i,l.getSeconds()))}if(r){var c=void 0!==h.config.minTime?h.config.minTime:h.config.minDate;(n=Math.max(n,c.getHours()))===c.getHours()&&(a=Math.max(a,c.getMinutes())),a===c.getMinutes()&&(i=Math.max(i,c.getSeconds()))}O(n,a,i)}}function I(e){var t=e||h.latestSelectedDateObj;t&&O(t.getHours(),t.getMinutes(),t.getSeconds())}function S(){var e=h.config.defaultHour,t=h.config.defaultMinute,n=h.config.defaultSeconds;if(void 0!==h.config.minDate){var a=h.config.minDate.getHours(),i=h.config.minDate.getMinutes();(e=Math.max(e,a))===a&&(t=Math.max(i,t)),e===a&&t===i&&(n=h.config.minDate.getSeconds())}if(void 0!==h.config.maxDate){var o=h.config.maxDate.getHours(),r=h.config.maxDate.getMinutes();(e=Math.min(e,o))===o&&(t=Math.min(r,t)),e===o&&t===r&&(n=h.config.maxDate.getSeconds())}O(e,t,n)}function O(e,t,n){void 0!==h.latestSelectedDateObj&&h.latestSelectedDateObj.setHours(e%24,t,n||0,0),h.hourElement&&h.minuteElement&&!h.isMobile&&(h.hourElement.value=i(h.config.time_24hr?e:(12+e)%12+12*o(e%12==0)),h.minuteElement.value=i(t),void 0!==h.amPM&&(h.amPM.textContent=h.l10n.amPM[o(e>=12)]),void 0!==h.secondElement&&(h.secondElement.value=i(n)))}function _(e){var t=parseInt(e.target.value)+(e.delta||0);(t/1e3>1||"Enter"===e.key&&!/[^\d]/.test(t.toString()))&&Q(t)}function F(e,t,n,a){return t instanceof Array?t.forEach(function(t){return F(e,t,n,a)}):e instanceof Array?e.forEach(function(e){return F(e,t,n,a)}):(e.addEventListener(t,n,a),void h._handlers.push({element:e,event:t,handler:n,options:a}))}function N(e){return function(t){1===t.which&&e(t)}}function Y(){ge("onChange")}function A(e,t){var n=void 0!==e?h.parseDate(e):h.latestSelectedDateObj||(h.config.minDate&&h.config.minDate>h.now?h.config.minDate:h.config.maxDate&&h.config.maxDate=0&&w(e,h.selectedDates[1])<=0}(t)&&!he(t)&&o.classList.add("inRange"),h.weekNumbers&&1===h.config.showMonths&&"prevMonthDay"!==e&&n%7==1&&h.weekNumbers.insertAdjacentHTML("beforeend",""+h.config.getWeek(t)+""),ge("onDayCreate",o),o}function L(e){e.focus(),"range"===h.config.mode&&ne(e)}function W(e){for(var t=e>0?0:h.config.showMonths-1,n=e>0?h.config.showMonths:-1,a=t;a!=n;a+=e)for(var i=h.daysContainer.children[a],o=e>0?0:i.children.length-1,r=e>0?i.children.length:-1,l=o;l!=r;l+=e){var c=i.children[l];if(-1===c.className.indexOf("hidden")&&X(c.dateObj))return c}}function R(e,t){var n=ee(document.activeElement||document.body),a=void 0!==e?e:n?document.activeElement:void 0!==h.selectedDateElem&&ee(h.selectedDateElem)?h.selectedDateElem:void 0!==h.todayDateElem&&ee(h.todayDateElem)?h.todayDateElem:W(t>0?1:-1);return void 0===a?h._input.focus():n?void function(e,t){for(var n=-1===e.className.indexOf("Month")?e.dateObj.getMonth():h.currentMonth,a=t>0?h.config.showMonths:-1,i=t>0?1:-1,o=n-h.currentMonth;o!=a;o+=i)for(var r=h.daysContainer.children[o],l=n-h.currentMonth===o?e.$i+t:t<0?r.children.length-1:0,c=r.children.length,d=l;d>=0&&d0?c:-1);d+=i){var s=r.children[d];if(-1===s.className.indexOf("hidden")&&X(s.dateObj)&&Math.abs(e.$i-d)>=Math.abs(t))return L(s)}h.changeMonth(i),R(W(i),0)}(a,t):L(a)}function B(e,t){for(var n=(new Date(e,t,1).getDay()-h.l10n.firstDayOfWeek+7)%7,a=h.utils.getDaysInMonth((t-1+12)%12),i=h.utils.getDaysInMonth(t),o=window.document.createDocumentFragment(),r=h.config.showMonths>1,l=r?"prevMonthDay hidden":"prevMonthDay",c=r?"nextMonthDay hidden":"nextMonthDay",s=a+1-n,u=0;s<=a;s++,u++)o.appendChild(H(l,new Date(e,t-1,s),s,u));for(s=1;s<=i;s++,u++)o.appendChild(H("",new Date(e,t,s),s,u));for(var f=i+1;f<=42-n&&(1===h.config.showMonths||u%7!=0);f++,u++)o.appendChild(H(c,new Date(e,t+1,f%i),f,u));var m=d("div","dayContainer");return m.appendChild(o),m}function J(){if(void 0!==h.daysContainer){s(h.daysContainer),h.weekNumbers&&s(h.weekNumbers);for(var e=document.createDocumentFragment(),t=0;t1||"dropdown"!==h.config.monthSelectorType)){var e=function(e){return!(void 0!==h.config.minDate&&h.currentYear===h.config.minDate.getFullYear()&&eh.config.maxDate.getMonth())};h.monthsDropdownContainer.tabIndex=-1,h.monthsDropdownContainer.innerHTML="";for(var t=0;t<12;t++)if(e(t)){var n=d("option","flatpickr-monthDropdown-month");n.value=new Date(h.currentYear,t).getMonth().toString(),n.textContent=m(t,h.config.shorthandCurrentMonth,h.l10n),n.tabIndex=-1,h.currentMonth===t&&(n.selected=!0),h.monthsDropdownContainer.appendChild(n)}}}function U(){var e,t=d("div","flatpickr-month"),n=window.document.createDocumentFragment();h.config.showMonths>1||"static"===h.config.monthSelectorType?e=d("span","cur-month"):(h.monthsDropdownContainer=d("select","flatpickr-monthDropdown-months"),F(h.monthsDropdownContainer,"change",function(e){var t=e.target,n=parseInt(t.value,10);h.changeMonth(n-h.currentMonth),ge("onMonthChange")}),K(),e=h.monthsDropdownContainer);var a=u("cur-year",{tabindex:"-1"}),i=a.getElementsByTagName("input")[0];i.setAttribute("aria-label",h.l10n.yearAriaLabel),h.config.minDate&&i.setAttribute("min",h.config.minDate.getFullYear().toString()),h.config.maxDate&&(i.setAttribute("max",h.config.maxDate.getFullYear().toString()),i.disabled=!!h.config.minDate&&h.config.minDate.getFullYear()===h.config.maxDate.getFullYear());var o=d("div","flatpickr-current-month");return o.appendChild(e),o.appendChild(a),n.appendChild(o),t.appendChild(n),{container:t,yearElement:i,monthElement:e}}function q(){s(h.monthNav),h.monthNav.appendChild(h.prevMonthNav),h.config.showMonths&&(h.yearElements=[],h.monthElements=[]);for(var e=h.config.showMonths;e--;){var t=U();h.yearElements.push(t.yearElement),h.monthElements.push(t.monthElement),h.monthNav.appendChild(t.container)}h.monthNav.appendChild(h.nextMonthNav)}function $(){h.weekdayContainer?s(h.weekdayContainer):h.weekdayContainer=d("div","flatpickr-weekdays");for(var e=h.config.showMonths;e--;){var t=d("div","flatpickr-weekdaycontainer");h.weekdayContainer.appendChild(t)}return z(),h.weekdayContainer}function z(){if(h.weekdayContainer){var e=h.l10n.firstDayOfWeek,t=h.l10n.weekdays.shorthand.slice();e>0&&e\n "+t.join("")+"\n \n "}}function G(e,t){void 0===t&&(t=!0);var n=t?e:e-h.currentMonth;n<0&&!0===h._hidePrevMonthArrow||n>0&&!0===h._hideNextMonthArrow||(h.currentMonth+=n,(h.currentMonth<0||h.currentMonth>11)&&(h.currentYear+=h.currentMonth>11?1:-1,h.currentMonth=(h.currentMonth+12)%12,ge("onYearChange"),K()),J(),ge("onMonthChange"),ve())}function V(e){return!(!h.config.appendTo||!h.config.appendTo.contains(e))||h.calendarContainer.contains(e)}function Z(e){if(h.isOpen&&!h.config.inline){var t="function"==typeof(r=e).composedPath?r.composedPath()[0]:r.target,n=V(t),a=t===h.input||t===h.altInput||h.element.contains(t)||e.path&&e.path.indexOf&&(~e.path.indexOf(h.input)||~e.path.indexOf(h.altInput)),i="blur"===e.type?a&&e.relatedTarget&&!V(e.relatedTarget):!a&&!n&&!V(e.relatedTarget),o=!h.config.ignoredFocusElements.some(function(e){return e.contains(t)});i&&o&&(void 0!==h.timeContainer&&void 0!==h.minuteElement&&void 0!==h.hourElement&&T(),h.close(),"range"===h.config.mode&&1===h.selectedDates.length&&(h.clear(!1),h.redraw()))}var r}function Q(e){if(!(!e||h.config.minDate&&eh.config.maxDate.getFullYear())){var t=e,n=h.currentYear!==t;h.currentYear=t||h.currentYear,h.config.maxDate&&h.currentYear===h.config.maxDate.getFullYear()?h.currentMonth=Math.min(h.config.maxDate.getMonth(),h.currentMonth):h.config.minDate&&h.currentYear===h.config.minDate.getFullYear()&&(h.currentMonth=Math.max(h.config.minDate.getMonth(),h.currentMonth)),n&&(h.redraw(),ge("onYearChange"),K())}}function X(e,t){void 0===t&&(t=!0);var n=h.parseDate(e,void 0,t);if(h.config.minDate&&n&&w(n,h.config.minDate,void 0!==t?t:!h.minDateHasTime)<0||h.config.maxDate&&n&&w(n,h.config.maxDate,void 0!==t?t:!h.maxDateHasTime)>0)return!1;if(0===h.config.enable.length&&0===h.config.disable.length)return!0;if(void 0===n)return!1;for(var a=h.config.enable.length>0,i=a?h.config.enable:h.config.disable,o=0,r=void 0;o=r.from.getTime()&&n.getTime()<=r.to.getTime())return a}return!a}function ee(e){return void 0!==h.daysContainer&&(-1===e.className.indexOf("hidden")&&h.daysContainer.contains(e))}function te(e){var t=e.target===h._input,n=h.config.allowInput,a=h.isOpen&&(!n||!t),i=h.config.inline&&t&&!n;if(13===e.keyCode&&t){if(n)return h.setDate(h._input.value,!0,e.target===h.altInput?h.config.altFormat:h.config.dateFormat),e.target.blur();h.open()}else if(V(e.target)||a||i){var o=!!h.timeContainer&&h.timeContainer.contains(e.target);switch(e.keyCode){case 13:o?(e.preventDefault(),T(),de()):se(e);break;case 27:e.preventDefault(),de();break;case 8:case 46:t&&!h.config.allowInput&&(e.preventDefault(),h.clear());break;case 37:case 39:if(o||t)h.hourElement&&h.hourElement.focus();else if(e.preventDefault(),void 0!==h.daysContainer&&(!1===n||document.activeElement&&ee(document.activeElement))){var r=39===e.keyCode?1:-1;e.ctrlKey?(e.stopPropagation(),G(r),R(W(1),0)):R(void 0,r)}break;case 38:case 40:e.preventDefault();var l=40===e.keyCode?1:-1;h.daysContainer&&void 0!==e.target.$i||e.target===h.input||e.target===h.altInput?e.ctrlKey?(e.stopPropagation(),Q(h.currentYear-l),R(W(1),0)):o||R(void 0,7*l):e.target===h.currentYearElement?Q(h.currentYear-l):h.config.enableTime&&(!o&&h.hourElement&&h.hourElement.focus(),T(e),h._debouncedChange());break;case 9:if(o){var c=[h.hourElement,h.minuteElement,h.secondElement,h.amPM].concat(h.pluginElements).filter(function(e){return e}),d=c.indexOf(e.target);if(-1!==d){var s=c[d+(e.shiftKey?-1:1)];e.preventDefault(),(s||h._input).focus()}}else!h.config.noCalendar&&h.daysContainer&&h.daysContainer.contains(e.target)&&e.shiftKey&&(e.preventDefault(),h._input.focus())}}if(void 0!==h.amPM&&e.target===h.amPM)switch(e.key){case h.l10n.amPM[0].charAt(0):case h.l10n.amPM[0].charAt(0).toLowerCase():h.amPM.textContent=h.l10n.amPM[0],k(),we();break;case h.l10n.amPM[1].charAt(0):case h.l10n.amPM[1].charAt(0).toLowerCase():h.amPM.textContent=h.l10n.amPM[1],k(),we()}(t||V(e.target))&&ge("onKeyDown",e)}function ne(e){if(1===h.selectedDates.length&&(!e||e.classList.contains("flatpickr-day")&&!e.classList.contains("flatpickr-disabled"))){for(var t=e?e.dateObj.getTime():h.days.firstElementChild.dateObj.getTime(),n=h.parseDate(h.selectedDates[0],void 0,!0).getTime(),a=Math.min(t,h.selectedDates[0].getTime()),i=Math.max(t,h.selectedDates[0].getTime()),o=!1,r=0,l=0,c=a;ca&&cr)?r=c:c>n&&(!l||c0&&d0&&d>l;return u?(c.classList.add("notAllowed"),["inRange","startRange","endRange"].forEach(function(e){c.classList.remove(e)}),"continue"):o&&!u?"continue":(["startRange","inRange","endRange","notAllowed"].forEach(function(e){c.classList.remove(e)}),void(void 0!==e&&(e.classList.add(t<=h.selectedDates[0].getTime()?"startRange":"endRange"),nt&&d===n&&c.classList.add("endRange"),d>=r&&(0===l||d<=l)&&b(d,n,t)&&c.classList.add("inRange"))))},f=0,m=s.children.length;f0||n.getMinutes()>0||n.getSeconds()>0),h.selectedDates&&(h.selectedDates=h.selectedDates.filter(function(e){return X(e)}),h.selectedDates.length||"min"!==e||I(n),we()),h.daysContainer&&(ce(),void 0!==n?h.currentYearElement[e]=n.getFullYear().toString():h.currentYearElement.removeAttribute(e),h.currentYearElement.disabled=!!a&&void 0!==n&&a.getFullYear()===n.getFullYear())}}function re(){"object"!=typeof h.config.locale&&void 0===E.l10ns[h.config.locale]&&h.config.errorHandler(new Error("flatpickr: invalid locale "+h.config.locale)),h.l10n=e({},E.l10ns.default,"object"==typeof h.config.locale?h.config.locale:"default"!==h.config.locale?E.l10ns[h.config.locale]:void 0),p.K="("+h.l10n.amPM[0]+"|"+h.l10n.amPM[1]+"|"+h.l10n.amPM[0].toLowerCase()+"|"+h.l10n.amPM[1].toLowerCase()+")",void 0===e({},g,JSON.parse(JSON.stringify(f.dataset||{}))).time_24hr&&void 0===E.defaultConfig.time_24hr&&(h.config.time_24hr=h.l10n.time_24hr),h.formatDate=v(h),h.parseDate=D({config:h.config,l10n:h.l10n})}function le(e){if(void 0!==h.calendarContainer){ge("onPreCalendarPosition");var t=e||h._positionElement,n=Array.prototype.reduce.call(h.calendarContainer.children,function(e,t){return e+t.offsetHeight},0),a=h.calendarContainer.offsetWidth,i=h.config.position.split(" "),o=i[0],r=i.length>1?i[1]:null,l=t.getBoundingClientRect(),d=window.innerHeight-l.bottom,s="above"===o||"below"!==o&&dn,u=window.pageYOffset+l.top+(s?-n-2:t.offsetHeight+2);if(c(h.calendarContainer,"arrowTop",!s),c(h.calendarContainer,"arrowBottom",s),!h.config.inline){var f=window.pageXOffset+l.left-(null!=r&&"center"===r?(a-l.width)/2:0),m=window.document.body.offsetWidth-(window.pageXOffset+l.right),g=f+a>window.document.body.offsetWidth,p=m+a>window.document.body.offsetWidth;if(c(h.calendarContainer,"rightMost",g),!h.config.static)if(h.calendarContainer.style.top=u+"px",g)if(p){var v=document.styleSheets[0];if(void 0===v)return;var D=window.document.body.offsetWidth,w=Math.max(0,D/2-a/2),b=v.cssRules.length,C="{left:"+l.left+"px;right:auto;}";c(h.calendarContainer,"rightMost",!1),c(h.calendarContainer,"centerMost",!0),v.insertRule(".flatpickr-calendar.centerMost:before,.flatpickr-calendar.centerMost:after"+C,b),h.calendarContainer.style.left=w+"px",h.calendarContainer.style.right="auto"}else h.calendarContainer.style.left="auto",h.calendarContainer.style.right=m+"px";else h.calendarContainer.style.left=f+"px",h.calendarContainer.style.right="auto"}}}function ce(){h.config.noCalendar||h.isMobile||(ve(),J())}function de(){h._input.focus(),-1!==window.navigator.userAgent.indexOf("MSIE")||void 0!==navigator.msMaxTouchPoints?setTimeout(h.close,0):h.close()}function se(e){e.preventDefault(),e.stopPropagation();var t=function e(t,n){return n(t)?t:t.parentNode?e(t.parentNode,n):void 0}(e.target,function(e){return e.classList&&e.classList.contains("flatpickr-day")&&!e.classList.contains("flatpickr-disabled")&&!e.classList.contains("notAllowed")});if(void 0!==t){var n=t,a=h.latestSelectedDateObj=new Date(n.dateObj.getTime()),i=(a.getMonth()h.currentMonth+h.config.showMonths-1)&&"range"!==h.config.mode;if(h.selectedDateElem=n,"single"===h.config.mode)h.selectedDates=[a];else if("multiple"===h.config.mode){var o=he(a);o?h.selectedDates.splice(parseInt(o),1):h.selectedDates.push(a)}else"range"===h.config.mode&&(2===h.selectedDates.length&&h.clear(!1,!1),h.latestSelectedDateObj=a,h.selectedDates.push(a),0!==w(a,h.selectedDates[0],!0)&&h.selectedDates.sort(function(e,t){return e.getTime()-t.getTime()}));if(k(),i){var r=h.currentYear!==a.getFullYear();h.currentYear=a.getFullYear(),h.currentMonth=a.getMonth(),r&&(ge("onYearChange"),K()),ge("onMonthChange")}if(ve(),J(),we(),h.config.enableTime&&setTimeout(function(){return h.showTimeInput=!0},50),i||"range"===h.config.mode||1!==h.config.showMonths?void 0!==h.selectedDateElem&&void 0===h.hourElement&&h.selectedDateElem&&h.selectedDateElem.focus():L(n),void 0!==h.hourElement&&void 0!==h.hourElement&&h.hourElement.focus(),h.config.closeOnSelect){var l="single"===h.config.mode&&!h.config.enableTime,c="range"===h.config.mode&&2===h.selectedDates.length&&!h.config.enableTime;(l||c)&&de()}Y()}}h.parseDate=D({config:h.config,l10n:h.l10n}),h._handlers=[],h.pluginElements=[],h.loadedPlugins=[],h._bind=F,h._setHoursFromDate=I,h._positionCalendar=le,h.changeMonth=G,h.changeYear=Q,h.clear=function(e,t){void 0===e&&(e=!0);void 0===t&&(t=!0);h.input.value="",void 0!==h.altInput&&(h.altInput.value="");void 0!==h.mobileInput&&(h.mobileInput.value="");h.selectedDates=[],h.latestSelectedDateObj=void 0,!0===t&&(h.currentYear=h._initialDate.getFullYear(),h.currentMonth=h._initialDate.getMonth());h.showTimeInput=!1,!0===h.config.enableTime&&S();h.redraw(),e&&ge("onChange")},h.close=function(){h.isOpen=!1,h.isMobile||(void 0!==h.calendarContainer&&h.calendarContainer.classList.remove("open"),void 0!==h._input&&h._input.classList.remove("active"));ge("onClose")},h._createElement=d,h.destroy=function(){void 0!==h.config&&ge("onDestroy");for(var e=h._handlers.length;e--;){var t=h._handlers[e];t.element.removeEventListener(t.event,t.handler,t.options)}if(h._handlers=[],h.mobileInput)h.mobileInput.parentNode&&h.mobileInput.parentNode.removeChild(h.mobileInput),h.mobileInput=void 0;else if(h.calendarContainer&&h.calendarContainer.parentNode)if(h.config.static&&h.calendarContainer.parentNode){var n=h.calendarContainer.parentNode;if(n.lastChild&&n.removeChild(n.lastChild),n.parentNode){for(;n.firstChild;)n.parentNode.insertBefore(n.firstChild,n);n.parentNode.removeChild(n)}}else h.calendarContainer.parentNode.removeChild(h.calendarContainer);h.altInput&&(h.input.type="text",h.altInput.parentNode&&h.altInput.parentNode.removeChild(h.altInput),delete h.altInput);h.input&&(h.input.type=h.input._type,h.input.classList.remove("flatpickr-input"),h.input.removeAttribute("readonly"),h.input.value="");["_showTimeInput","latestSelectedDateObj","_hideNextMonthArrow","_hidePrevMonthArrow","__hideNextMonthArrow","__hidePrevMonthArrow","isMobile","isOpen","selectedDateElem","minDateHasTime","maxDateHasTime","days","daysContainer","_input","_positionElement","innerContainer","rContainer","monthNav","todayDateElem","calendarContainer","weekdayContainer","prevMonthNav","nextMonthNav","monthsDropdownContainer","currentMonthElement","currentYearElement","navigationCurrentMonth","selectedDateElem","config"].forEach(function(e){try{delete h[e]}catch(e){}})},h.isEnabled=X,h.jumpToDate=A,h.open=function(e,t){void 0===t&&(t=h._positionElement);if(!0===h.isMobile)return e&&(e.preventDefault(),e.target&&e.target.blur()),void 0!==h.mobileInput&&(h.mobileInput.focus(),h.mobileInput.click()),void ge("onOpen");if(h._input.disabled||h.config.inline)return;var n=h.isOpen;h.isOpen=!0,n||(h.calendarContainer.classList.add("open"),h._input.classList.add("active"),ge("onOpen"),le(t));!0===h.config.enableTime&&!0===h.config.noCalendar&&(0===h.selectedDates.length&&ie(),!1!==h.config.allowInput||void 0!==e&&h.timeContainer.contains(e.relatedTarget)||setTimeout(function(){return h.hourElement.select()},50))},h.redraw=ce,h.set=function(e,n){if(null!==e&&"object"==typeof e)for(var a in Object.assign(h.config,e),e)void 0!==ue[a]&&ue[a].forEach(function(e){return e()});else h.config[e]=n,void 0!==ue[e]?ue[e].forEach(function(e){return e()}):t.indexOf(e)>-1&&(h.config[e]=l(n));h.redraw(),we(!1)},h.setDate=function(e,t,n){void 0===t&&(t=!1);void 0===n&&(n=h.config.dateFormat);if(0!==e&&!e||e instanceof Array&&0===e.length)return h.clear(t);fe(e,n),h.showTimeInput=h.selectedDates.length>0,h.latestSelectedDateObj=h.selectedDates[h.selectedDates.length-1],h.redraw(),A(),I(),0===h.selectedDates.length&&h.clear(!1);we(t),t&&ge("onChange")},h.toggle=function(e){if(!0===h.isOpen)return h.close();h.open(e)};var ue={locale:[re,z],showMonths:[q,x,$],minDate:[A],maxDate:[A]};function fe(e,t){var n=[];if(e instanceof Array)n=e.map(function(e){return h.parseDate(e,t)});else if(e instanceof Date||"number"==typeof e)n=[h.parseDate(e,t)];else if("string"==typeof e)switch(h.config.mode){case"single":case"time":n=[h.parseDate(e,t)];break;case"multiple":n=e.split(h.config.conjunction).map(function(e){return h.parseDate(e,t)});break;case"range":n=e.split(h.l10n.rangeSeparator).map(function(e){return h.parseDate(e,t)})}else h.config.errorHandler(new Error("Invalid date supplied: "+JSON.stringify(e)));h.selectedDates=n.filter(function(e){return e instanceof Date&&X(e,!1)}),"range"===h.config.mode&&h.selectedDates.sort(function(e,t){return e.getTime()-t.getTime()})}function me(e){return e.slice().map(function(e){return"string"==typeof e||"number"==typeof e||e instanceof Date?h.parseDate(e,void 0,!0):e&&"object"==typeof e&&e.from&&e.to?{from:h.parseDate(e.from,void 0),to:h.parseDate(e.to,void 0)}:e}).filter(function(e){return e})}function ge(e,t){if(void 0!==h.config){var n=h.config[e];if(void 0!==n&&n.length>0)for(var a=0;n[a]&&a1||"static"===h.config.monthSelectorType?h.monthElements[t].textContent=m(n.getMonth(),h.config.shorthandCurrentMonth,h.l10n)+" ":h.monthsDropdownContainer.value=n.getMonth().toString(),e.value=n.getFullYear().toString()}),h._hidePrevMonthArrow=void 0!==h.config.minDate&&(h.currentYear===h.config.minDate.getFullYear()?h.currentMonth<=h.config.minDate.getMonth():h.currentYearh.config.maxDate.getMonth():h.currentYear>h.config.maxDate.getFullYear()))}function De(e){return h.selectedDates.map(function(t){return h.formatDate(t,e)}).filter(function(e,t,n){return"range"!==h.config.mode||h.config.enableTime||n.indexOf(e)===t}).join("range"!==h.config.mode?h.config.conjunction:h.l10n.rangeSeparator)}function we(e){void 0===e&&(e=!0),void 0!==h.mobileInput&&h.mobileFormatStr&&(h.mobileInput.value=void 0!==h.latestSelectedDateObj?h.formatDate(h.latestSelectedDateObj,h.mobileFormatStr):""),h.input.value=De(h.config.dateFormat),void 0!==h.altInput&&(h.altInput.value=De(h.config.altFormat)),!1!==e&&ge("onValueUpdate")}function be(e){var t=h.prevMonthNav.contains(e.target),n=h.nextMonthNav.contains(e.target);t||n?G(t?-1:1):h.yearElements.indexOf(e.target)>=0?e.target.select():e.target.classList.contains("arrowUp")?h.changeYear(h.currentYear+1):e.target.classList.contains("arrowDown")&&h.changeYear(h.currentYear-1)}return function(){h.element=h.input=f,h.isOpen=!1,function(){var a=["wrap","weekNumbers","allowInput","clickOpens","time_24hr","enableTime","noCalendar","altInput","shorthandCurrentMonth","inline","static","enableSeconds","disableMobile"],i=e({},g,JSON.parse(JSON.stringify(f.dataset||{}))),o={};h.config.parseDate=i.parseDate,h.config.formatDate=i.formatDate,Object.defineProperty(h.config,"enable",{get:function(){return h.config._enable},set:function(e){h.config._enable=me(e)}}),Object.defineProperty(h.config,"disable",{get:function(){return h.config._disable},set:function(e){h.config._disable=me(e)}});var r="time"===i.mode;if(!i.dateFormat&&(i.enableTime||r)){var c=E.defaultConfig.dateFormat||n.dateFormat;o.dateFormat=i.noCalendar||r?"H:i"+(i.enableSeconds?":S":""):c+" H:i"+(i.enableSeconds?":S":"")}if(i.altInput&&(i.enableTime||r)&&!i.altFormat){var d=E.defaultConfig.altFormat||n.altFormat;o.altFormat=i.noCalendar||r?"h:i"+(i.enableSeconds?":S K":" K"):d+" h:i"+(i.enableSeconds?":S":"")+" K"}i.altInputClass||(h.config.altInputClass=h.input.className+" "+h.config.altInputClass),Object.defineProperty(h.config,"minDate",{get:function(){return h.config._minDate},set:oe("min")}),Object.defineProperty(h.config,"maxDate",{get:function(){return h.config._maxDate},set:oe("max")});var s=function(e){return function(t){h.config["min"===e?"_minTime":"_maxTime"]=h.parseDate(t,"H:i:S")}};Object.defineProperty(h.config,"minTime",{get:function(){return h.config._minTime},set:s("min")}),Object.defineProperty(h.config,"maxTime",{get:function(){return h.config._maxTime},set:s("max")}),"time"===i.mode&&(h.config.noCalendar=!0,h.config.enableTime=!0),Object.assign(h.config,o,i);for(var u=0;u-1?h.config[p]=l(m[p]).map(y).concat(h.config[p]):void 0===i[p]&&(h.config[p]=m[p])}ge("onParseConfig")}(),re(),h.input=h.config.wrap?f.querySelector("[data-input]"):f,h.input?(h.input._type=h.input.type,h.input.type="text",h.input.classList.add("flatpickr-input"),h._input=h.input,h.config.altInput&&(h.altInput=d(h.input.nodeName,h.config.altInputClass),h._input=h.altInput,h.altInput.placeholder=h.input.placeholder,h.altInput.disabled=h.input.disabled,h.altInput.required=h.input.required,h.altInput.tabIndex=h.input.tabIndex,h.altInput.type="text",h.input.setAttribute("type","hidden"),!h.config.static&&h.input.parentNode&&h.input.parentNode.insertBefore(h.altInput,h.input.nextSibling)),h.config.allowInput||h._input.setAttribute("readonly","readonly"),h._positionElement=h.config.positionElement||h._input):h.config.errorHandler(new Error("Invalid input element specified")),function(){h.selectedDates=[],h.now=h.parseDate(h.config.now)||new Date;var e=h.config.defaultDate||("INPUT"!==h.input.nodeName&&"TEXTAREA"!==h.input.nodeName||!h.input.placeholder||h.input.value!==h.input.placeholder?h.input.value:null);e&&fe(e,h.config.dateFormat),h._initialDate=h.selectedDates.length>0?h.selectedDates[0]:h.config.minDate&&h.config.minDate.getTime()>h.now.getTime()?h.config.minDate:h.config.maxDate&&h.config.maxDate.getTime()0&&(h.latestSelectedDateObj=h.selectedDates[0]),void 0!==h.config.minTime&&(h.config.minTime=h.parseDate(h.config.minTime,"H:i")),void 0!==h.config.maxTime&&(h.config.maxTime=h.parseDate(h.config.maxTime,"H:i")),h.minDateHasTime=!!h.config.minDate&&(h.config.minDate.getHours()>0||h.config.minDate.getMinutes()>0||h.config.minDate.getSeconds()>0),h.maxDateHasTime=!!h.config.maxDate&&(h.config.maxDate.getHours()>0||h.config.maxDate.getMinutes()>0||h.config.maxDate.getSeconds()>0),Object.defineProperty(h,"showTimeInput",{get:function(){return h._showTimeInput},set:function(e){h._showTimeInput=e,h.calendarContainer&&c(h.calendarContainer,"showTimeInput",e),h.isOpen&&le()}})}(),h.utils={getDaysInMonth:function(e,t){return void 0===e&&(e=h.currentMonth),void 0===t&&(t=h.currentYear),1===e&&(t%4==0&&t%100!=0||t%400==0)?29:h.l10n.daysInMonth[e]}},h.isMobile||function(){var e=window.document.createDocumentFragment();if(h.calendarContainer=d("div","flatpickr-calendar"),h.calendarContainer.tabIndex=-1,!h.config.noCalendar){if(e.appendChild((h.monthNav=d("div","flatpickr-months"),h.yearElements=[],h.monthElements=[],h.prevMonthNav=d("span","flatpickr-prev-month"),h.prevMonthNav.innerHTML=h.config.prevArrow,h.nextMonthNav=d("span","flatpickr-next-month"),h.nextMonthNav.innerHTML=h.config.nextArrow,q(),Object.defineProperty(h,"_hidePrevMonthArrow",{get:function(){return h.__hidePrevMonthArrow},set:function(e){h.__hidePrevMonthArrow!==e&&(c(h.prevMonthNav,"flatpickr-disabled",e),h.__hidePrevMonthArrow=e)}}),Object.defineProperty(h,"_hideNextMonthArrow",{get:function(){return h.__hideNextMonthArrow},set:function(e){h.__hideNextMonthArrow!==e&&(c(h.nextMonthNav,"flatpickr-disabled",e),h.__hideNextMonthArrow=e)}}),h.currentYearElement=h.yearElements[0],ve(),h.monthNav)),h.innerContainer=d("div","flatpickr-innerContainer"),h.config.weekNumbers){var t=function(){h.calendarContainer.classList.add("hasWeeks");var e=d("div","flatpickr-weekwrapper");e.appendChild(d("span","flatpickr-weekday",h.l10n.weekAbbreviation));var t=d("div","flatpickr-weeks");return e.appendChild(t),{weekWrapper:e,weekNumbers:t}}(),n=t.weekWrapper,a=t.weekNumbers;h.innerContainer.appendChild(n),h.weekNumbers=a,h.weekWrapper=n}h.rContainer=d("div","flatpickr-rContainer"),h.rContainer.appendChild($()),h.daysContainer||(h.daysContainer=d("div","flatpickr-days"),h.daysContainer.tabIndex=-1),J(),h.rContainer.appendChild(h.daysContainer),h.innerContainer.appendChild(h.rContainer),e.appendChild(h.innerContainer)}h.config.enableTime&&e.appendChild(function(){h.calendarContainer.classList.add("hasTime"),h.config.noCalendar&&h.calendarContainer.classList.add("noCalendar"),h.timeContainer=d("div","flatpickr-time"),h.timeContainer.tabIndex=-1;var e=d("span","flatpickr-time-separator",":"),t=u("flatpickr-hour",{"aria-label":h.l10n.hourAriaLabel});h.hourElement=t.getElementsByTagName("input")[0];var n=u("flatpickr-minute",{"aria-label":h.l10n.minuteAriaLabel});if(h.minuteElement=n.getElementsByTagName("input")[0],h.hourElement.tabIndex=h.minuteElement.tabIndex=-1,h.hourElement.value=i(h.latestSelectedDateObj?h.latestSelectedDateObj.getHours():h.config.time_24hr?h.config.defaultHour:function(e){switch(e%24){case 0:case 12:return 12;default:return e%12}}(h.config.defaultHour)),h.minuteElement.value=i(h.latestSelectedDateObj?h.latestSelectedDateObj.getMinutes():h.config.defaultMinute),h.hourElement.setAttribute("step",h.config.hourIncrement.toString()),h.minuteElement.setAttribute("step",h.config.minuteIncrement.toString()),h.hourElement.setAttribute("min",h.config.time_24hr?"0":"1"),h.hourElement.setAttribute("max",h.config.time_24hr?"23":"12"),h.minuteElement.setAttribute("min","0"),h.minuteElement.setAttribute("max","59"),h.timeContainer.appendChild(t),h.timeContainer.appendChild(e),h.timeContainer.appendChild(n),h.config.time_24hr&&h.timeContainer.classList.add("time24hr"),h.config.enableSeconds){h.timeContainer.classList.add("hasSeconds");var a=u("flatpickr-second");h.secondElement=a.getElementsByTagName("input")[0],h.secondElement.value=i(h.latestSelectedDateObj?h.latestSelectedDateObj.getSeconds():h.config.defaultSeconds),h.secondElement.setAttribute("step",h.minuteElement.getAttribute("step")),h.secondElement.setAttribute("min","0"),h.secondElement.setAttribute("max","59"),h.timeContainer.appendChild(d("span","flatpickr-time-separator",":")),h.timeContainer.appendChild(a)}return h.config.time_24hr||(h.amPM=d("span","flatpickr-am-pm",h.l10n.amPM[o((h.latestSelectedDateObj?h.hourElement.value:h.config.defaultHour)>11)]),h.amPM.title=h.l10n.toggleTitle,h.amPM.tabIndex=-1,h.timeContainer.appendChild(h.amPM)),h.timeContainer}()),c(h.calendarContainer,"rangeMode","range"===h.config.mode),c(h.calendarContainer,"animate",!0===h.config.animate),c(h.calendarContainer,"multiMonth",h.config.showMonths>1),h.calendarContainer.appendChild(e);var r=void 0!==h.config.appendTo&&void 0!==h.config.appendTo.nodeType;if((h.config.inline||h.config.static)&&(h.calendarContainer.classList.add(h.config.inline?"inline":"static"),h.config.inline&&(!r&&h.element.parentNode?h.element.parentNode.insertBefore(h.calendarContainer,h._input.nextSibling):void 0!==h.config.appendTo&&h.config.appendTo.appendChild(h.calendarContainer)),h.config.static)){var l=d("div","flatpickr-wrapper");h.element.parentNode&&h.element.parentNode.insertBefore(l,h.element),l.appendChild(h.element),h.altInput&&l.appendChild(h.altInput),l.appendChild(h.calendarContainer)}h.config.static||h.config.inline||(void 0!==h.config.appendTo?h.config.appendTo:window.document.body).appendChild(h.calendarContainer)}(),function(){if(h.config.wrap&&["open","close","toggle","clear"].forEach(function(e){Array.prototype.forEach.call(h.element.querySelectorAll("[data-"+e+"]"),function(t){return F(t,"click",h[e])})}),h.isMobile)!function(){var e=h.config.enableTime?h.config.noCalendar?"time":"datetime-local":"date";h.mobileInput=d("input",h.input.className+" flatpickr-mobile"),h.mobileInput.step=h.input.getAttribute("step")||"any",h.mobileInput.tabIndex=1,h.mobileInput.type=e,h.mobileInput.disabled=h.input.disabled,h.mobileInput.required=h.input.required,h.mobileInput.placeholder=h.input.placeholder,h.mobileFormatStr="datetime-local"===e?"Y-m-d\\TH:i:S":"date"===e?"Y-m-d":"H:i:S",h.selectedDates.length>0&&(h.mobileInput.defaultValue=h.mobileInput.value=h.formatDate(h.selectedDates[0],h.mobileFormatStr)),h.config.minDate&&(h.mobileInput.min=h.formatDate(h.config.minDate,"Y-m-d")),h.config.maxDate&&(h.mobileInput.max=h.formatDate(h.config.maxDate,"Y-m-d")),h.input.type="hidden",void 0!==h.altInput&&(h.altInput.type="hidden");try{h.input.parentNode&&h.input.parentNode.insertBefore(h.mobileInput,h.input.nextSibling)}catch(e){}F(h.mobileInput,"change",function(e){h.setDate(e.target.value,!1,h.mobileFormatStr),ge("onChange"),ge("onClose")})}();else{var e=r(ae,50);h._debouncedChange=r(Y,M),h.daysContainer&&!/iPhone|iPad|iPod/i.test(navigator.userAgent)&&F(h.daysContainer,"mouseover",function(e){"range"===h.config.mode&&ne(e.target)}),F(window.document.body,"keydown",te),h.config.inline||h.config.static||F(window,"resize",e),void 0!==window.ontouchstart?F(window.document,"touchstart",Z):F(window.document,"mousedown",N(Z)),F(window.document,"focus",Z,{capture:!0}),!0===h.config.clickOpens&&(F(h._input,"focus",h.open),F(h._input,"mousedown",N(h.open))),void 0!==h.daysContainer&&(F(h.monthNav,"mousedown",N(be)),F(h.monthNav,["keyup","increment"],_),F(h.daysContainer,"mousedown",N(se))),void 0!==h.timeContainer&&void 0!==h.minuteElement&&void 0!==h.hourElement&&(F(h.timeContainer,["increment"],T),F(h.timeContainer,"blur",T,{capture:!0}),F(h.timeContainer,"mousedown",N(P)),F([h.hourElement,h.minuteElement],["focus","click"],function(e){return e.target.select()}),void 0!==h.secondElement&&F(h.secondElement,"focus",function(){return h.secondElement&&h.secondElement.select()}),void 0!==h.amPM&&F(h.amPM,"mousedown",N(function(e){T(e),Y()})))}}(),(h.selectedDates.length||h.config.noCalendar)&&(h.config.enableTime&&I(h.config.noCalendar?h.latestSelectedDateObj||h.config.minDate:void 0),we(!1)),x(),h.showTimeInput=h.selectedDates.length>0||h.config.noCalendar;var a=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);!h.isMobile&&a&&le(),ge("onReady")}(),h}function x(e,t){for(var n=Array.prototype.slice.call(e).filter(function(e){return e instanceof HTMLElement}),a=[],i=0;i Date: Sat, 28 Dec 2019 21:32:40 +0000 Subject: [PATCH 23/59] Include Flatpickr library globally --- netbox/templates/_base.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index 0dfd7c861..b1a713f4e 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -9,6 +9,7 @@ + @@ -69,6 +70,7 @@ +